Appearance
CSRF 防护
跨站请求伪造(Cross-Site Request Forgery,CSRF)是一种攻击方式,攻击者诱导用户在已认证的 Web 应用程序上执行非预期的操作。
CSRF 攻击原理
CSRF 攻击利用了浏览器的自动发送 Cookie 和认证凭据的特性。当用户登录了某个网站后,浏览器会自动在后续请求中包含该网站的认证信息,攻击者利用这一点来伪造用户请求。
攻击示例:
假设用户已登录银行网站 bank.example.com,并且有一个转账功能:
html
<!-- 银行转账页面 -->
<form action="https://bank.example.com/transfer" method="POST">
<input type="hidden" name="toAccount" value="attacker_account" />
<input type="hidden" name="amount" value="10000" />
<input type="submit" value="Transfer Money" />
</form>
攻击者可能会创建一个恶意页面:
html
<!-- 恶意网站上的隐藏表单 -->
<html>
<head>
<title>Free Gift!</title>
</head>
<body>
<img src="https://bank.example.com/transfer?toAccount=attacker&amount=10000" width="0" height="0" />
<!-- 或者使用表单自动提交 -->
<form id="csrfForm" action="https://bank.example.com/transfer" method="POST">
<input type="hidden" name="toAccount" value="attacker_account" />
<input type="hidden" name="amount" value="10000" />
</form>
<script>document.getElementById('csrfForm').submit();</script>
</body>
</html>
当用户访问这个恶意页面时,如果他们仍处于银行网站的登录状态,转账操作就会自动执行。
CSRF 攻击类型
GET 请求攻击
通过图像标签、iframe 等发起 GET 请求:
html
<img src="https://bank.example.com/transfer?toAccount=attacker&amount=10000" />
POST 请求攻击
通过自动提交的表单发起 POST 请求:
html
<form action="https://bank.example.com/transfer" method="POST">
<input type="hidden" name="toAccount" value="attacker" />
<input type="hidden" name="amount" value="10000" />
</form>
<script>document.forms[0].submit();</script>
CSRF 防护措施
1. CSRF Token
在表单中添加随机生成的令牌:
html
<form action="/transfer" method="POST">
<input type="hidden" name="csrf_token" value="random_generated_token" />
<input type="text" name="toAccount" />
<input type="text" name="amount" />
<button type="submit">Transfer</button>
</form>
服务器端验证令牌:
javascript
// Express.js 示例
app.post('/transfer', (req, res) => {
const { csrf_token, toAccount, amount } = req.body;
// 验证 CSRF token
if (!validateCsrfToken(csrf_token, req.session)) {
return res.status(403).send('Invalid CSRF token');
}
// 执行转账操作
transferMoney(toAccount, amount);
});
2. SameSite Cookie 属性
设置 Cookie 的 SameSite 属性:
Set-Cookie: sessionId=abc123; SameSite=Strict
SameSite 属性的值:
Strict:完全禁止跨站请求携带 CookieLax:允许部分跨站请求(如链接跳转)None:允许所有跨站请求(需要 Secure 属性)
3. 双重提交 Cookie
将 CSRF token 同时存储在 Cookie 和表单中,服务器验证两者是否匹配:
javascript
// 设置 CSRF token 到 Cookie
res.cookie('csrf_token', generateCsrfToken(), { httpOnly: false });
// 在表单中也需要包含同样的 token
const csrfToken = req.cookies.csrf_token;
if (csrfToken !== req.body.csrf_token) {
return res.status(403).send('CSRF token mismatch');
}
4. 验证 Referer 头部
检查请求的来源:
javascript
app.use((req, res, next) => {
const allowedReferers = ['https://yourdomain.com'];
const referer = req.get('Referer');
if (referer && !allowedReferers.some(allowed => referer.startsWith(allowed))) {
return res.status(403).send('Invalid referer');
}
next();
});
5. 自定义头部验证
要求请求包含自定义头部:
javascript
// 前端 JavaScript
fetch('/api/transfer', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-Token': getCsrfToken()
},
body: JSON.stringify({ toAccount, amount })
});
框架中的 CSRF 防护
Express.js with csurf
javascript
const csrf = require('csurf');
const csrfProtection = csrf({ cookie: true });
app.use(csrfProtection);
app.get('/form', (req, res) => {
res.render('send-money-form', { csrfToken: req.csrfToken() });
});
app.post('/transfer', csrfProtection, (req, res) => {
// 处理转账请求
});
Django
Django 自带 CSRF 中间件:
python
from django.views.decorators.csrf import csrf_protect
from django.middleware.csrf import csrf_exempt
@csrf_protect
def transfer_view(request):
if request.method == 'POST':
# 处理转账请求
pass
在模板中:
html
{% csrf_token %}
<form method="post">
{% csrf_token %}
<!-- 表单内容 -->
</form>
ASP.NET Core
csharp
public class TransferController : Controller
{
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Transfer(TransferModel model)
{
// 处理转账请求
return View();
}
}
在视图中:
html
<form method="post" asp-action="Transfer">
@Html.AntiForgeryToken()
<!-- 表单内容 -->
</form>
检测 CSRF 漏洞
手动测试
- 登录目标网站
- 打开另一个浏览器窗口(无登录状态)
- 尝试直接访问需要认证的操作
- 检查是否可以绕过身份验证
自动化工具
- OWASP ZAP
- Burp Suite
- CSRFTester
最佳实践
- 使用 CSRF Token - 对所有修改状态的请求使用 CSRF 令牌
- 设置 SameSite Cookie - 为认证 Cookie 设置适当的 SameSite 属性
- 验证请求来源 - 检查 Origin 和 Referer 头部
- 区分敏感操作 - 对敏感操作实施额外的安全检查
- 定期评估 - 定期审查和测试 CSRF 防护措施
- 安全培训 - 确保开发团队了解 CSRF 风险和防护措施
总结
CSRF 是一种常见的 Web 安全威胁,但可以通过多种技术手段有效防护。最佳做法是结合使用多种防护措施,创建多层安全防护体系。