什么是双重提交 Cookie 防护 CSRF 的原理和实现方式?
双重提交 Cookie(Double Submit Cookie)是一种 CSRF 防护技术,它通过在 Cookie 和请求参数中同时存储相同的 Token 来验证请求的合法性。双重提交 Cookie 的基本原理Token 生成:服务器生成一个随机的 CSRF Token双重存储:Token 同时存储在 Cookie 和请求参数中验证逻辑:服务器验证 Cookie 中的 Token 和请求参数中的 Token 是否匹配实现步骤1. 生成 Tokenfunction generateCSRFToken() { return crypto.randomBytes(32).toString('hex');}// 中间件:生成并设置 Tokenfunction 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<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. 验证 Tokenfunction 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();}工作原理为什么双重提交有效?同源策略:恶意网站无法读取目标网站的 Cookie跨站请求限制:恶意网站无法在请求参数中包含正确的 Token匹配验证:只有同源请求才能同时访问 Cookie 和设置请求参数攻击场景分析<!-- 恶意网站尝试发起 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服务器验证失败,拒绝请求优势无需服务器状态:不需要在服务器端存储 Token易于实现:实现相对简单可扩展性:适合分布式系统性能好:不需要查询数据库或 Session局限性Cookie 安全性:如果 Cookie 被窃取(XSS),防护失效需要配合 HttpOnly 使用(但 JavaScript 无法读取)子域名风险:如果子域名存在 XSS 漏洞,可能影响主域名需要谨慎设置 Cookie 的域属性Token 泄露:如果 Token 在 URL 中暴露,可能被记录在日志中应该使用 POST 请求传递 Token最佳实践1. 结合其他防护措施app.use(helmet()); // XSS 防护app.use(cookieSession({ secret: 'secret', cookie: { httpOnly: true, secure: true, sameSite: 'strict' }}));app.use(csrfTokenMiddleware);app.use(csrfProtection);2. Token 刷新策略// 每次请求后刷新 Tokenfunction 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. 安全配置// 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 防护技术,特别适合分布式系统和需要高性能的场景。但应该与其他安全措施配合使用,提供全面的安全保护。