文章目录
前端容易遭受的攻击及解决方案
前端容易遭受以下几种攻击:
XSS(跨站脚本攻击)
- 攻击原理:攻击者通过在目标网站中注入恶意脚本(通常是JavaScript),当用户访问被注入恶意脚本的页面时,浏览器会执行这些脚本,从而导致用户信息泄露、账号被盗用等安全问题。例如,攻击者在评论区插入一段脚本,当其他用户查看评论时,脚本会窃取用户的登录凭证并发送给攻击者。
- 解决方案:
- 输入验证和过滤:对用户输入的内容进行严格验证和过滤,例如限制输入的字符类型、长度,去除或转义可能导致脚本执行的特殊字符,如
<
、>
、&
、'
、"
等。可以使用一些成熟的库来进行输入过滤,如DOMPurify,它可以帮助净化HTML,防止XSS攻击。 - 输出编码:在将用户输入的内容或者服务器端返回的数据展示在页面上时,进行适当的编码。例如,使用JavaScript的
encodeURIComponent
函数对URL参数进行编码,使用textContent
而不是innerHTML
来插入文本内容,这样可以防止浏览器将数据中的脚本内容进行解析和执行。 - 设置Content-Security-Policy(CSP):CSP是一种浏览器安全机制,通过设置合适的策略,可以限制页面加载的资源来源,如脚本、样式表、图片等。例如,配置
Content - Security - Policy: default - src 'self'; script - src 'self' https://trusted - scripts.com;
,这表示默认情况下只允许加载来自本域的资源,脚本可以额外从https://trusted - scripts.com
加载,从而防止恶意脚本的注入。
- 输入验证和过滤:对用户输入的内容进行严格验证和过滤,例如限制输入的字符类型、长度,去除或转义可能导致脚本执行的特殊字符,如
CSRF(跨站请求伪造)
- 攻击原理:攻击者利用用户在目标网站已登录的状态,诱导用户访问恶意网站,该恶意网站会向目标网站发送伪造的请求。由于用户的浏览器会自动带上目标网站的登录凭证(如cookies),目标网站会误以为是用户的合法请求而执行操作。例如,攻击者构造一个恶意链接,当用户点击该链接时,会在用户不知情的情况下向用户的银行网站发送转账请求。
- 解决方案:
- 使用CSRF Tokens:在服务器端为每个用户生成一个唯一的CSRF令牌,并将其包含在每个表单或者重要的请求中。当用户提交请求时,服务器会验证这个令牌是否有效。例如,在一个HTML表单中,可以添加一个隐藏字段来存放CSRF令牌:
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
,然后在服务器端验证这个令牌是否和用户会话中的令牌一致。 - Same - Site Cookies属性设置:将Cookie的
Same - Site
属性设置为Strict
或Lax
。Strict
模式下,浏览器只会在请求来自设置Cookie的同一站点并且是顶级导航(用户直接在浏览器地址栏输入网址、点击书签等)时才会发送Cookie;Lax
模式稍微宽松一些,在一些安全的跨站请求场景下也会发送Cookie,如通过GET
方法的链接跳转。这可以在一定程度上防止CSRF攻击,因为攻击者无法轻易利用用户的Cookie来发送伪造请求。
- 使用CSRF Tokens:在服务器端为每个用户生成一个唯一的CSRF令牌,并将其包含在每个表单或者重要的请求中。当用户提交请求时,服务器会验证这个令牌是否有效。例如,在一个HTML表单中,可以添加一个隐藏字段来存放CSRF令牌:
点击劫持
- 攻击原理:攻击者通过将目标网站嵌入到一个透明的
iframe
中,然后在上面覆盖一个诱骗用户点击的元素。当用户以为是在点击正常的内容时,实际上是在点击被隐藏的目标网站中的按钮或者链接,从而执行一些非预期的操作。例如,攻击者将一个银行转账确认按钮隐藏在一个游戏广告的下面,当用户点击广告时,实际上触发了银行转账操作。 - 解决方案:
- 设置X - Frame - Options响应头:服务器可以在响应头中设置
X - Frame - Options
来控制页面是否允许被其他网站通过iframe
嵌入。其值可以是DENY
(完全不允许嵌入)、SAMEORIGIN
(只允许同域嵌入)。例如,在一个Node.js应用中,使用Express框架可以这样设置:app.use((req, res, next) => { res.set('X - Frame - Options', 'SAMEORIGIN'); next(); });
- 使用JavaScript防御:通过在页面加载时检测是否被嵌入到
iframe
中,并采取相应的措施,如改变页面显示内容或者直接跳转。例如,以下是一段简单的JavaScript代码用于检测是否在iframe
中:if (window.self!== window.top) { window.top.location.href = window.location.href; }
。这会在页面发现自己被嵌入到iframe
中时,将顶级窗口的地址替换为自己的地址,从而防止点击劫持。
- 设置X - Frame - Options响应头:服务器可以在响应头中设置
解决方案实例场景还原
XSS(跨站脚本攻击)防范案例
- 案例场景:一个在线论坛网站允许用户发表评论和文章内容。攻击者尝试通过在评论区插入恶意脚本来窃取其他用户的登录信息。
- 防范措施与实现:
- 输入过滤和净化:网站使用了DOMPurify库来对用户输入的评论内容进行净化。例如,在后端接收用户评论的代码中,在存储评论到数据库之前,会调用DOMPurify来处理评论内容。假设网站使用Node.js和Express框架,代码可能如下:
const express = require('express');
const DOMPurify = require('dompurify');
const { JSDOM } = require('jsdom');
const app = express();
const window = new JSDOM('').window;
const purify = DOMPurify(window);
app.post('/submit-comment', (req, res) => {
let comment = req.body.comment;
// 净化评论内容
comment = purify.sanitize(comment);
// 将净化后的评论存储到数据库等操作
//...
res.send('Comment submitted successfully.');
});
- 输出编码:在前端展示评论内容时,使用
textContent
来确保内容以纯文本形式展示,避免脚本执行。例如,在HTML文件中有一个用于展示评论的<div>
元素:
<div id="comment-container"></div>
<script>
// 假设从服务器获取到评论数据并存储在变量comments中
const comments = ["<script>alert('XSS attempt')</script>", "这是一条正常评论"];
const commentContainer = document.getElementById('comment-container');
comments.forEach((comment) => {
const p = document.createElement('p');
p.textContent = comment;
commentContainer.appendChild(p);
});
</script>
- Content - Security - Policy(CSP)设置:网站的服务器在响应头中设置了CSP策略。例如,在服务器配置中(以Nginx为例)添加以下配置来限制脚本来源:
add_header Content-Security-Policy "script-src' self'
https://cdnjs.cloudflare.com/ajax/libs/dompurify/2.4.3/purify.min.js;";
这表示脚本只能从本域和指定的cdnjs.cloudflare.com
域名(用于加载DOMPurify库)加载,从而防止其他恶意脚本的注入。
CSRF(跨站请求伪造)防范案例
- 案例场景:一个银行网站有转账功能,用户登录后可以进行转账操作。攻击者试图通过构造恶意网站来诱导用户在不知情的情况下进行转账。
- 防范措施与实现:
- CSRF Tokens使用:在银行网站的转账表单中,服务器为每个用户生成一个唯一的CSRF令牌。例如,在后端使用Python的Flask框架,代码如下:
from flask import Flask, request, session, redirect, url_for, render_template
import uuid
app = Flask(__name__)
app.secret_key = 'your_secret_key'
@app.route('/transfer', methods=['GET', 'POST'])
def transfer():
if request.method == 'GET':
# 生成CSRF令牌并存储在用户会话中
session['csrf_token'] = str(uuid.uuid4())
return render_template('transfer.html', csrf_token=session['csrf_token'])
elif request.method == 'POST':
# 验证CSRF令牌
if request.form.get('csrf_token') == session.get('csrf_token'):
# 执行转账操作
#...
return 'Transfer successful.'
else:
return 'CSRF token validation failed.'
在对应的transfer.html
模板文件中,表单包含了这个CSRF令牌:
<form action="/transfer" method="post">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<label for="amount">转账金额:</label>
<input type="number" id="amount" name="amount" required>
<label for="recipient">收款人:</label>
<input type="text" id="recipient" name="recipient" required>
<input type="submit" value="转账">
</form>
- Same - Site Cookies属性设置:银行网站将登录相关的Cookie的
Same - Site
属性设置为Strict
。在服务器端(以Java Spring Boot为例),可以通过配置CookieSerializer
来实现:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.web.savedrequest.NullRequestCache;
import org.springframework.session.web.http.CookieSerializer;
import org.springframework.session.web.http.DefaultCookieSerializer;
@Configuration
public class SessionConfig {
@Bean
public CookieSerializer cookieSerializer() {
DefaultCookieSerializer serializer = new DefaultCookieSerializer();
serializer.setSameSite("Strict");
return serializer;
}
}
这使得浏览器在处理跨站请求时不会轻易发送登录相关的Cookie,降低了CSRF攻击的风险。
点击劫持防范案例
- 案例场景:一个电商网站有购买商品的按钮,攻击者想通过点击劫持让用户在不知情的情况下购买商品。
- 防范措施与实现:
- 设置X - Frame - Options响应头:电商网站的服务器在响应头中设置
X - Frame - Options
为SAMEORIGIN
。在Node.js的Express应用中,可以这样设置:
- 设置X - Frame - Options响应头:电商网站的服务器在响应头中设置
const express = require('express');
const app = express();
app.use((req, res, next) => {
res.set('X - Frame - Options', 'SAMEORIGIN');
next();
});
- JavaScript防御:在电商网站的前端页面加载时,添加检测是否被嵌入到
iframe
中的代码。例如,在index.html
文件中:
<script>
if (window.self!== window.top) {
window.top.location.href = window.location.href;
}
</script>
这可以防止电商网站的页面被恶意嵌入到其他网站的iframe
中,从而避免点击劫持攻击。
DOMPurify 库 应用实例
-
基本文本净化
- 场景:有一个用户评论区,用户可以输入评论内容,这些内容会显示在网页上。为了防止XSS攻击,需要对用户输入的内容进行净化。
- 代码示例:
- 首先,在HTML文件中,假设有一个用于显示评论的
<div>
元素:<div id="comment-section"></div>
- 在JavaScript文件中,假设从服务器获取到用户评论数据存储在一个数组
comments
中,并且已经引入了DOMPurify
库。以下是使用DOMPurify
净化评论并显示的代码:const comments = ["<script>alert('XSS attempt')</script>", "这是一条正常评论"]; const commentSection = document.getElementById('comment-section'); const purify = DOMPurify.sanitize; comments.forEach((comment) => { const p = document.createElement('p'); p.innerHTML = purify(comment); commentSection.appendChild(p); });
- 在这个例子中,当遇到包含恶意脚本(如第一个评论中的
<script>alert('XSS attempt')</script>
)的评论时,DOMPurify
会将脚本部分去除或者转义,从而防止脚本在浏览器中执行。
- 首先,在HTML文件中,假设有一个用于显示评论的
-
净化HTML片段
- 场景:在一个富文本编辑器中,用户可以编辑包含HTML标签的内容,如文章、博客等。在将这些内容展示在网页上之前,需要净化其中可能包含的恶意代码。
- 代码示例:
- 假设从富文本编辑器获取到的内容存储在变量
htmlContent
中,例如:const htmlContent = '<p>这是一段正常内容。<a href="https://example.com">链接</a></p><script>maliciousScript() </script>'; const purify = DOMPurify.sanitize; const purifiedContent = purify(htmlContent); const contentContainer = document.createElement('div'); contentContainer.innerHTML = purifiedContent; document.body.appendChild(contentContainer);
DOMPurify
会识别并去除其中的恶意脚本部分(<script>maliciousScript()</script>
),同时保留合法的HTML标签和内容,如<p>
标签和<a>
标签等,然后将净化后的内容安全地显示在网页上。
- 假设从富文本编辑器获取到的内容存储在变量
-
与React或Vue等框架结合使用
-
React示例场景:在一个React应用中,有一个组件用于显示从服务器获取的用户生成的HTML内容。
-
代码示例:
- 首先,安装
DOMPurify
库:npm install dompurify
。 - 在React组件中,假设从服务器获取的内容存储在组件的
state
中的userContent
属性中,以下是净化并显示内容的代码:import React, { Component } from 'react'; import DOMPurify from 'dompurify'; class UserContentDisplay extends Component { constructor(props) { super(props); this.state = { userContent: '<p>这是用户内容。<img src="https://example.com/image.jpg"> </p><script>alert("XSS")</script>' }; } render() { const purify = DOMPurify.sanitize; const purifiedContent = purify(this.state.userContent); return ( <div dangerouslySetInnerHTML={{__html: purifiedContent}}></div> ); } } export default UserContentDisplay;
- 在这个React组件中,通过
dangerouslySetInnerHTML
来显示净化后的HTML内容。DOMPurify
会在渲染之前对内容进行净化,防止其中的恶意脚本被执行。
- 首先,安装
-
Vue示例场景:在一个Vue应用中,同样有一个组件用于展示用户生成的HTML内容。
-
代码示例:
- 安装
DOMPurify
库后,在Vue组件中,假设数据存储在data
选项中的userContent
属性中:<template> <div v - html="purifiedContent"></div> </template> <script> import DOMPurify from 'dompurify'; export default { data() { return { userContent: '<p>这是用户内容。<img src="https://example.com/image.jpg" ></p> <script>alert("XSS")</script>' }; }, computed: { purifiedContent() { const purify = DOMPurify.sanitize; return purify(this.userContent); } } }; </script>
- 在Vue组件中,通过
v - html
指令来显示净化后的内容。DOMPurify
在computed
属性中对内容进行净化,确保在显示时不会出现XSS问题。
- 安装
-
encodeURIComponent 函数
-
参数解释
encodeURIComponent()
函数用于将字符串作为URI(统一资源标识符)组件进行编码。它接受一个参数,这个参数是要进行编码的字符串。- 它会将字符串中的特殊字符转换为对应的UTF - 8编码格式的十六进制转义序列。特殊字符包括但不限于非字母数字字符,如
;
、/
、?
、:
、@
、&
、=
、+
、$
、,
、#
、[
、]
。这样可以确保在将这些字符串用于构建URL等场景时,它们不会被错误地解析,同时也能防止一些安全漏洞,比如XSS(跨站脚本攻击)通过URL参数注入恶意脚本。
-
应用实例
-
场景一:构建查询参数的URL
- 需求:构建一个包含查询参数的URL,用于向服务器发送搜索请求。例如,要搜索关键词为“JavaScript教程”的内容。
- 代码示例:
let keyword = "JavaScript教程"; let encodedKeyword = encodeURIComponent(keyword); let url = "https://example.com/search?q=" + encodedKeyword; console.log(url);
- 在这里,
encodeURIComponent()
函数将keyword
字符串中的中文字符和其他特殊字符(如果有的话)进行编码。例如,编码后的JavaScript教程
可能会变成JavaScript%E6%95%99%E7%A8%8B
(实际编码结果取决于字符的UTF - 8编码)。然后将编码后的字符串拼接在URL的查询参数部分,这样就可以安全地构建一个包含查询参数的URL,并且服务器在接收这个参数时能够正确地解析它。
- 在这里,
-
场景二:防止XSS攻击通过URL参数注入恶意脚本
- 需求:假设在一个Web应用中,有一个页面通过URL参数接收用户输入,并将这个输入显示在页面上。为了防止恶意用户通过URL参数注入脚本,需要对输入进行编码。
- 代码示例:
function displayUserInput() { let userInputFromUrl = window.location.search.slice(1); let decodedInput = decodeURIComponent(userInputFromUrl); let encodedInput = encodeURIComponent(decodedInput); let displayElement = document.getElementById("user-input-display"); displayElement.textContent = "您输入的内容是:" + encodedInput; } displayUserInput();
- 首先,从
window.location.search
获取URL中的查询参数部分(去除?
符号),并通过decodeURIComponent()
函数进行解码(如果之前已经编码过)。然后,使用encodeURIComponent()
函数对解码后的用户输入进行编码,最后将编码后的内容显示在页面上。这样,即使用户在URL参数中输入了类似<script>alert('恶意脚本')</script>
这样的内容,经过编码后,它会变成%3Cscript%3Ealert%28%27恶意脚本%27%29%3C%2Fscript%3E
,在显示时就不会被浏览器解析为脚本而执行,从而防止了XSS攻击。
- 首先,从
-