跨站点请求伪造(Cross-Site Request Forgery,CSRF 或 XSRF)是一种常见的网络攻击方式。攻击者诱使用户在已认证的网站上执行非预期的操作,从而利用用户的身份和权限进行恶意操作。CSRF 攻击通常通过伪造请求(如表单提交或 API 调用)来实现。


CSRF 攻击的原理

  1. 用户登录:用户登录到一个受信任的网站(如银行网站),并获得了会话凭证(如 Cookie)。
  2. 诱导用户访问恶意网站:攻击者诱使用户访问一个恶意网站,该网站包含伪造的请求。
  3. 伪造请求:恶意网站向受信任的网站发送请求(如转账请求),浏览器会自动携带用户的会话凭证。
  4. 服务器执行请求:服务器无法区分请求是来自用户还是攻击者,因此会执行该请求。

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 攻击的危害

  1. 未经授权的操作:攻击者可以利用用户的身份执行敏感操作(如转账、修改密码、删除数据等)。
  2. 数据泄露:攻击者可以窃取用户的敏感信息。
  3. 破坏系统:攻击者可以篡改或删除数据,导致系统不可用。

防御 CSRF 攻击的措施

为了防止 CSRF 攻击,开发者需要采取多层次的安全措施:

1. 使用 CSRF Token

  • 在每个表单或请求中包含一个随机生成的 CSRF Token,并在服务器端验证该 Token。
  • 示例:
     
     // 在 .NET Core 中生成和验证 CSRF Token
     [ValidateAntiForgeryToken]
     public IActionResult Transfer(TransferModel model)
     {
         // 处理转账逻辑
     }

2. 验证请求来源

  • 检查请求头中的 RefererOrigin,确保请求来自受信任的域名。
  • 示例:
     if (Request.Headers["Origin"] != "https://trusted.com")
     {
         return BadRequest("Invalid request origin");
     }

3. 使用 SameSite Cookie

  • 设置 Cookie 的 SameSite 属性为 StrictLax,防止跨站点请求携带 Cookie。
  • 示例:
     Set-Cookie: sessionId=abc123; SameSite=Strict; Secure; HttpOnly

4. 双重验证

  • 对于敏感操作(如转账、修改密码),要求用户进行双重验证(如输入密码或验证码)。

5. 限制请求方法

  • 对于敏感操作,只允许使用 POSTPUTDELETE 等方法,禁止使用 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 攻击。开发者应始终将安全性作为开发流程的重要组成部分,并定期进行安全测试和代码审查。

最后修改日期: 2025年1月14日

作者