跨站点请求伪造(Cross-Site Request Forgery,CSRF 或 XSRF)是一种常见的网络攻击方式。攻击者诱使用户在已认证的网站上执行非预期的操作,从而利用用户的身份和权限进行恶意操作。CSRF 攻击通常通过伪造请求(如表单提交或 API 调用)来实现。
CSRF 攻击的原理
- 用户登录:用户登录到一个受信任的网站(如银行网站),并获得了会话凭证(如 Cookie)。
- 诱导用户访问恶意网站:攻击者诱使用户访问一个恶意网站,该网站包含伪造的请求。
- 伪造请求:恶意网站向受信任的网站发送请求(如转账请求),浏览器会自动携带用户的会话凭证。
- 服务器执行请求:服务器无法区分请求是来自用户还是攻击者,因此会执行该请求。
CSRF 攻击的示例
假设有一个银行网站的转账接口:
POST /transfer HTTP/1.1
Host: bank.com
Content-Type: application/x-www-form-urlencoded
amount=1000&toAccount=attacker
攻击者可以在恶意网站中嵌入以下代码:
<form action="https://bank.com/transfer" method="POST">
<input type="hidden" name="amount" value="1000" />
<input type="hidden" name="toAccount" value="attacker" />
</form>
<script>
document.forms[0].submit();
</script>
当用户访问该恶意网站时,浏览器会自动发送转账请求,并携带用户的会话凭证,导致攻击成功。
CSRF 攻击的危害
- 未经授权的操作:攻击者可以利用用户的身份执行敏感操作(如转账、修改密码、删除数据等)。
- 数据泄露:攻击者可以窃取用户的敏感信息。
- 破坏系统:攻击者可以篡改或删除数据,导致系统不可用。
防御 CSRF 攻击的措施
为了防止 CSRF 攻击,开发者需要采取多层次的安全措施:
1. 使用 CSRF Token
- 在每个表单或请求中包含一个随机生成的 CSRF Token,并在服务器端验证该 Token。
- 示例:
// 在 .NET Core 中生成和验证 CSRF Token [ValidateAntiForgeryToken] public IActionResult Transfer(TransferModel model) { // 处理转账逻辑 }
2. 验证请求来源
- 检查请求头中的
Referer
或Origin
,确保请求来自受信任的域名。 - 示例:
if (Request.Headers["Origin"] != "https://trusted.com") { return BadRequest("Invalid request origin"); }
3. 使用 SameSite Cookie
- 设置 Cookie 的
SameSite
属性为Strict
或Lax
,防止跨站点请求携带 Cookie。 - 示例:
Set-Cookie: sessionId=abc123; SameSite=Strict; Secure; HttpOnly
4. 双重验证
- 对于敏感操作(如转账、修改密码),要求用户进行双重验证(如输入密码或验证码)。
5. 限制请求方法
- 对于敏感操作,只允许使用
POST
、PUT
、DELETE
等方法,禁止使用GET
方法。
6. 使用框架的内置防护
- 现代 Web 框架(如 .NET Core、Django、Spring)通常内置了 CSRF 防护机制。
- 确保使用框架的最新版本,并遵循其安全最佳实践。
示例:防御 CSRF 攻击的代码
前端(HTML + JavaScript)
<form action="/transfer" method="POST">
<input type="hidden" name="csrf_token" value="随机生成的Token" />
<input type="text" name="amount" />
<input type="text" name="toAccount" />
<button type="submit">Transfer</button>
</form>
后端(.NET Core)
// 生成 CSRF Token
[HttpGet]
public IActionResult Transfer()
{
ViewBag.CsrfToken = GenerateCsrfToken();
return View();
}
// 验证 CSRF Token
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult Transfer(TransferModel model)
{
// 处理转账逻辑
return Ok("Transfer successful");
}
private string GenerateCsrfToken()
{
var bytes = new byte[32];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(bytes);
}
return Convert.ToBase64String(bytes);
}
后端(Node.js + Express)
const csrf = require('csurf');
const cookieParser = require('cookie-parser');
const express = require('express');
const app = express();
app.use(cookieParser());
app.use(csrf({ cookie: true }));
app.get('/transfer', (req, res) => {
res.send(`
<form action="/transfer" method="POST">
<input type="hidden" name="_csrf" value="${req.csrfToken()}" />
<input type="text" name="amount" />
<input type="text" name="toAccount" />
<button type="submit">Transfer</button>
</form>
`);
});
app.post('/transfer', (req, res) => {
// 处理转账逻辑
res.send('Transfer successful');
});
app.listen(3000, () => console.log('Server started on port 3000'));
总结
CSRF 攻击是一种严重的安全威胁,但通过使用 CSRF Token、验证请求来源、设置 SameSite Cookie 和双重验证等措施,可以有效防御 CSRF 攻击。开发者应始终将安全性作为开发流程的重要组成部分,并定期进行安全测试和代码审查。