关于 CSRF Token 的安全性与下发时机
更新: 5/24/2026 字数: 0 字 时长: 0 分钟
这是两个非常关键的问题,理解清楚才算真正掌握 CSRF Token 的防御原理。
一、CSRF Token 能被黑客获取到吗?
结论先行:在正常的 CSRF 攻击场景下,黑客拿不到 Token;但在某些特定漏洞或不当实现下,Token 可能泄露,防御就会失效。
1. 为什么"正常情况下"黑客拿不到?
CSRF Token 的安全性本质上依赖浏览器的同源策略(Same-Origin Policy)。
- 当用户访问正规站点
bank.com时,Token 被嵌入在bank.com的 HTML 页面中。 - 当用户访问恶意站点
evil.com时,evil.com的 JavaScript 无法读取bank.com页面的内容——这是浏览器底层强制隔离的。 - 因此
evil.com即使能让浏览器自动发请求(携带 Cookie),也没办法在请求里塞入正确的 Token。
这就是为什么 Cookie 不能单独防 CSRF,但 Token 可以——Cookie 浏览器会自动带,Token 必须由代码主动读取并放入请求。
2. 哪些情况下 Token 会泄露?
如果出现以下漏洞或错误实现,Token 就可能被黑客获取,CSRF 防御直接失守:
| 泄露途径 | 原理 | 防范方式 |
|---|---|---|
| XSS 漏洞 | 黑客在正规站点注入 JS,可直接读取页面中的 Token | 修复 XSS、输入输出过滤、CSP 策略 |
| Token 放在 URL 中 | 通过 Referer 头泄露给第三方站点,浏览器历史、日志也会记录 | Token 只放在请求体或自定义请求头,不放 URL |
| HTTP 明文传输 | 中间人可在网络层嗅探到 Token | 全站强制 HTTPS |
| Token 写入可被读取的 Cookie(无 HttpOnly) | XSS 时可被 document.cookie 读取 | Cookie 加 HttpOnly;或采用 Double Submit Cookie 时配合其他防护 |
| Token 跨子域共享但子域被攻破 | 攻击者控制子域后可读取 Token | Token 严格限定作用域,按主域隔离 |
| JSONP 接口返回 Token | 跨域脚本可通过 JSONP 窃取 | 禁止用 JSONP 返回敏感数据 |
CORS 配置过松(Access-Control-Allow-Origin: * 且允许凭证) | 任意站点可跨域读取响应中的 Token | 严格白名单,禁止通配符配合 credentials |
| Token 可预测 | 使用时间戳、自增 ID、弱随机算法生成 | 必须用密码学安全的随机数生成器(如 crypto.randomBytes、SecureRandom) |
关键认知:CSRF Token 防御 CSRF 的前提,是站点不存在 XSS 漏洞。一旦有 XSS,几乎所有 CSRF 防御(包括 SameSite Cookie)都会被绕过。所以业界常说:"XSS 是 CSRF 防御的天花板"。
二、CSRF Token 在什么时机下发?
下发时机的选择,要兼顾安全性、用户体验和实现复杂度。常见有以下几种策略:
策略 1:用户登录成功时下发(会话级 Token,最常见)
时机:用户登录认证通过后,服务端生成 Token,存入 Session,并通过响应返回给前端。
特点:
- 一次会话期间 Token 不变,前端可缓存复用。
- 实现简单,性能好。
- 用户登出或会话过期后 Token 失效,下次登录重新下发。
适用场景:绝大多数业务系统的默认选择。
策略 2:首次访问页面时下发(页面级 Token)
时机:用户请求任意一个需要 CSRF 防护的页面时,服务端在 HTML 中通过 <meta> 标签或隐藏表单字段注入 Token。
<meta name="csrf-token" content="随机字符串">
<!-- 或 -->
<input type="hidden" name="_csrf" value="随机字符串">前端 JS 从 DOM 中读取 Token,加入 AJAX 请求头(如 X-CSRF-Token)。
特点:
- 与后端模板渲染深度结合,典型如 Django、Spring Security、Laravel 的默认实现。
- 用户未登录时也可下发(用于防注册、登录表单本身被 CSRF)。
适用场景:服务端渲染(SSR)应用、传统 MVC 架构。
策略 3:前端按需请求接口获取(前后端分离场景)
时机:前端启动时(或首次发送敏感请求前)调用专门的接口(如 GET /api/csrf-token)获取 Token,服务端返回并同时写入 Session。
特点:
- 适配 SPA、移动端、前后端完全分离的架构。
- 前端把 Token 存在内存(不要存 LocalStorage,避免 XSS 风险),后续请求统一带上。
适用场景:React/Vue 等 SPA、API 网关、移动 App。
策略 4:每次敏感操作一次性 Token(One-Time Token)
时机:每次访问关键操作页面(如转账确认页)时,服务端生成一个只能使用一次的 Token,使用后立即作废。
特点:
- 安全性最高,即使 Token 被截获也无法重放。
- 实现复杂,需处理"用户打开多个标签页"等并发场景。
- 用户体验略差(页面刷新后旧 Token 失效)。
适用场景:金融交易、支付确认、密码修改等极高敏感操作。
策略 5:Token 定期轮换
时机:在会话有效期内,每隔一段时间(如 30 分钟)或在关键节点(如权限提升后)重新下发 Token。
特点:
- 即使旧 Token 因日志、缓存等渠道泄露,影响窗口有限。
- 需要前端配合更新逻辑。
适用场景:长会话、高安全等级系统。
三、生产环境推荐实践
| 场景 | 推荐方案 |
|---|---|
| 传统 Web 应用(服务端渲染) | 登录时下发 + 页面 <meta> 注入 + 会话级 Token |
| SPA / 前后端分离 | 启动时调接口获取 + 内存存储 + 自定义请求头携带 |
| 金融 / 支付类高敏感操作 | 会话级 Token + 关键操作一次性 Token + 二次验证(短信/密码) |
| 微服务 / 无状态架构 | Double Submit Cookie 模式,无需服务端存储 |
四、一句话总结
CSRF Token 的安全性建立在"浏览器同源策略 + 站点无 XSS漏洞 + Token 不可预测且不外泄"三个前提之上。 下发时机的核心原则是:在身份确认后尽早下发,存放在攻击者读不到的地方,并随会话生命周期管理——会话级是默认选择,一次性是高敏感场景的加强。