乐闻世界logo
搜索文章和话题

CSRF Token 是如何工作的?

2月21日 16:11

CSRF Token 是防御 CSRF 攻击最有效的方法之一,它通过验证请求的合法性来防止跨站请求伪造。

工作原理

1. Token 生成

  • 服务器在用户会话中生成随机 Token
  • Token 必须足够随机(建议至少 128 位)
  • Token 可以有时效性
  • Token 与用户会话绑定

2. Token 传递

  • Token 通过表单隐藏字段传递
  • 或通过自定义请求头传递(如 X-CSRF-Token
  • Token 也可以存储在 Cookie 中(双重提交)

3. Token 验证

  • 服务器收到请求后验证 Token
  • 检查请求中的 Token 是否与会话中的匹配
  • 验证 Token 是否过期
  • 验证 Token 是否已被使用(可选)

实现流程

shell
用户访问页面 → 服务器生成 CSRF Token → Token 存储在会话中 Token 添加到表单/请求头 → 用户提交表单/发送请求 服务器验证 Token → Token 匹配 → 执行操作 Token 不匹配 → 拒绝请求

代码实现

后端实现(Node.js + Express)

javascript
const express = require('express'); const crypto = require('crypto'); const session = require('express-session'); const app = express(); app.use(session({ secret: 'your-secret-key', resave: false, saveUninitialized: true })); // 生成 CSRF Token function generateCSRFToken() { return crypto.randomBytes(32).toString('hex'); } // 中间件:生成 Token app.use((req, res, next) => { if (!req.session.csrfToken) { req.session.csrfToken = generateCSRFToken(); } res.locals.csrfToken = req.session.csrfToken; next(); }); // 中间件:验证 Token function validateCSRFToken(req, res, next) { const token = req.body._csrf || req.headers['x-csrf-token']; if (!token || token !== req.session.csrfToken) { return res.status(403).json({ error: 'Invalid CSRF token' }); } next(); } // 受保护的路由 app.post('/transfer', validateCSRFToken, (req, res) => { // 处理转账逻辑 res.json({ success: true }); }); app.listen(3000);

前端实现

html
<!-- 表单提交 --> <form action="/transfer" method="POST"> <input type="hidden" name="_csrf" value="{{ csrfToken }}"> <input type="number" name="amount" placeholder="Amount"> <button type="submit">Transfer</button> </form> <!-- AJAX 请求 --> <script> const csrfToken = '{{ csrfToken }}'; fetch('/transfer', { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRF-Token': csrfToken }, body: JSON.stringify({ amount: 100 }) }) .then(response => response.json()) .then(data => console.log(data)); </script>

Spring Security 实现

java
@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .csrf() .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) .and() .authorizeRequests() .antMatchers("/public/**").permitAll() .anyRequest().authenticated(); } }

Django 实现

python
# settings.py MIDDLEWARE = [ 'django.middleware.csrf.CsrfViewMiddleware', # ... other middleware ] # 模板中使用 <form method="post"> {% csrf_token %} <input type="text" name="amount"> <button type="submit">Submit</button> </form> # 视图中验证 from django.views.decorators.csrf import csrf_protect @csrf_protect def transfer(request): if request.method == 'POST': # 处理转账逻辑 pass

Token 生成算法

使用加密安全的随机数生成器

javascript
// Node.js const crypto = require('crypto'); const token = crypto.randomBytes(32).toString('hex'); // Python import secrets token = secrets.token_hex(32) // Java import java.security.SecureRandom; import java.math.BigInteger; SecureRandom random = new SecureRandom(); String token = new BigInteger(130, random).toString(32);

安全注意事项

1. Token 随机性

  • 使用加密安全的随机数生成器
  • Token 长度至少 128 位(32 字节)
  • 避免使用可预测的 Token

2. Token 时效性

  • Token 应该有过期时间
  • 建议设置合理的过期时间(如 1-2 小时)
  • 敏感操作可以使用一次性 Token

3. Token 存储

  • Token 存储在服务器会话中
  • 不要在客户端存储敏感信息
  • 使用 HttpOnly Cookie 存储 Token(双重提交)

4. Token 传输

  • 使用 HTTPS 传输 Token
  • 避免在 URL 中传递 Token
  • 使用 POST 方法提交表单

5. Token 验证

  • 验证 Token 是否匹配
  • 验证 Token 是否过期
  • 验证请求来源(可选)

常见问题

1. Token 失效

  • 会话过期导致 Token 失效
  • 解决:自动刷新 Token 或延长会话时间

2. 多标签页问题

  • 多个标签页共享同一个 Token
  • 解决:每个标签页使用独立 Token 或共享 Token

3. AJAX 请求

  • 需要在请求头中添加 Token
  • 解决:使用拦截器自动添加 Token

4. 文件上传

  • 文件上传无法使用表单 Token
  • 解决:使用请求头或预签名 URL

最佳实践

  1. 使用框架内置的 CSRF 保护:如 Spring Security、Django
  2. Token 与会话绑定:每个会话使用独立 Token
  3. 设置合理的过期时间:平衡安全性和用户体验
  4. 记录 Token 使用情况:便于审计和监控
  5. 定期更新 Token:降低 Token 泄露风险
  6. 配合其他防护措施:如 SameSite Cookie、Origin 验证

总结

CSRF Token 通过验证请求的合法性有效防止 CSRF 攻击。正确实现 CSRF Token 需要注意 Token 的生成、存储、传输和验证等各个环节,确保整个流程的安全性。使用框架内置的 CSRF 保护功能可以大大简化实现过程。

标签:CSRF