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

什么是双重提交 Cookie 防护 CSRF 的原理和实现方式?

2月19日 19:15

双重提交 Cookie(Double Submit Cookie)是一种 CSRF 防护技术,它通过在 Cookie 和请求参数中同时存储相同的 Token 来验证请求的合法性。

  1. Token 生成:服务器生成一个随机的 CSRF Token
  2. 双重存储:Token 同时存储在 Cookie 和请求参数中
  3. 验证逻辑:服务器验证 Cookie 中的 Token 和请求参数中的 Token 是否匹配

实现步骤

1. 生成 Token

javascript
function generateCSRFToken() { return crypto.randomBytes(32).toString('hex'); } // 中间件:生成并设置 Token function csrfTokenMiddleware(req, res, next) { const token = generateCSRFToken(); res.cookie('csrfToken', token, { httpOnly: false, // JavaScript 需要读取 secure: true, sameSite: 'strict' }); res.locals.csrfToken = token; next(); }

2. 在表单中包含 Token

html
<form action="/api/submit" method="POST"> <input type="hidden" name="csrfToken" value="<%= csrfToken %>"> <!-- 其他表单字段 --> <button type="submit">提交</button> </form> <!-- 或者通过 JavaScript 设置 --> <script> const form = document.querySelector('form'); const csrfToken = document.querySelector('meta[name="csrf-token"]').content; const input = document.createElement('input'); input.type = 'hidden'; input.name = 'csrfToken'; input.value = csrfToken; form.appendChild(input); </script>

3. 验证 Token

javascript
function validateDoubleSubmitCookie(req) { const cookieToken = req.cookies.csrfToken; const paramToken = req.body.csrfToken || req.query.csrfToken; if (!cookieToken || !paramToken) { return false; } // 使用恒定时间比较防止时序攻击 return crypto.timingSafeEqual( Buffer.from(cookieToken), Buffer.from(paramToken) ); } // 验证中间件 function csrfProtection(req, res, next) { if (req.method === 'GET' || req.method === 'HEAD' || req.method === 'OPTIONS') { return next(); } if (!validateDoubleSubmitCookie(req)) { return res.status(403).send('CSRF token validation failed'); } next(); }

工作原理

为什么双重提交有效?

  1. 同源策略:恶意网站无法读取目标网站的 Cookie
  2. 跨站请求限制:恶意网站无法在请求参数中包含正确的 Token
  3. 匹配验证:只有同源请求才能同时访问 Cookie 和设置请求参数

攻击场景分析

html
<!-- 恶意网站尝试发起 CSRF 攻击 --> <form action="https://example.com/api/transfer" method="POST"> <input type="hidden" name="to" value="attacker"> <input type="hidden" name="amount" value="1000"> <!-- 无法获取正确的 csrfToken --> </form> <script> document.querySelector('form').submit(); </script>
  • 恶意网站可以发起请求
  • 浏览器会自动发送 Cookie 中的 Token
  • 但恶意网站无法在请求参数中包含正确的 Token
  • 服务器验证失败,拒绝请求

优势

  1. 无需服务器状态:不需要在服务器端存储 Token
  2. 易于实现:实现相对简单
  3. 可扩展性:适合分布式系统
  4. 性能好:不需要查询数据库或 Session

局限性

  1. Cookie 安全性

    • 如果 Cookie 被窃取(XSS),防护失效
    • 需要配合 HttpOnly 使用(但 JavaScript 无法读取)
  2. 子域名风险

    • 如果子域名存在 XSS 漏洞,可能影响主域名
    • 需要谨慎设置 Cookie 的域属性
  3. Token 泄露

    • 如果 Token 在 URL 中暴露,可能被记录在日志中
    • 应该使用 POST 请求传递 Token

最佳实践

1. 结合其他防护措施

javascript
app.use(helmet()); // XSS 防护 app.use(cookieSession({ secret: 'secret', cookie: { httpOnly: true, secure: true, sameSite: 'strict' } })); app.use(csrfTokenMiddleware); app.use(csrfProtection);

2. Token 刷新策略

javascript
// 每次请求后刷新 Token function refreshTokenMiddleware(req, res, next) { if (req.method !== 'GET' && req.method !== 'HEAD') { const newToken = generateCSRFToken(); res.cookie('csrfToken', newToken, { httpOnly: false, secure: true, sameSite: 'strict' }); res.locals.csrfToken = newToken; } next(); }

3. 安全配置

javascript
// Cookie 配置 res.cookie('csrfToken', token, { httpOnly: false, // 允许 JavaScript 读取 secure: true, // 仅 HTTPS sameSite: 'strict', // 最严格的同站策略 maxAge: 3600000, // 1小时过期 domain: '.example.com' // 谨慎设置域 });

与 CSRF Token 的对比

特性双重提交 Cookie传统 CSRF Token
服务器状态无需需要 Session
实现复杂度简单中等
分布式支持优秀需要共享 Session
安全性良好优秀
性能优秀良好

双重提交 Cookie 是一种有效的 CSRF 防护技术,特别适合分布式系统和需要高性能的场景。但应该与其他安全措施配合使用,提供全面的安全保护。

标签:CSRF