csrf令牌验证需服务端闭环控制生成、绑定、传递与校验四个环节;使用高熵随机数生成32字节令牌,强绑定会话并存于redis或session;前端依请求类型通过隐藏字段或x-csrf-token头传递;校验须前置业务逻辑,恒定时间比对,失败返回403;辅以samesite、二次验证等纵深防御。

实现CSRF令牌验证机制,关键在于服务端生成、绑定、传递与校验四个环节的闭环控制,确保每次敏感操作请求都携带且仅能使用一次有效的令牌。
令牌生成与存储
使用加密安全的随机数生成器(如 Python secrets、Go crypto/rand 或 PHP random_bytes)生成至少32字节(256位)的高熵令牌。生成后立即与当前用户会话强绑定:
- 存入服务端 session(如
$_SESSION['csrf_token']),或更推荐存入带过期时间的缓存(如 Redis,TTL 设为15–30分钟) - 避免在页面渲染时多次调用生成函数——必须在逻辑层统一生成一次,再同步写入 session 和输出到前端,防止前后端令牌不一致
- 同一会话可支持多令牌(例如每个表单独立发一个),但旧令牌应立即失效,禁用重放
前端携带方式
根据请求类型选择安全、不易泄露的传递路径:
-
HTML 表单:通过隐藏字段提交,如
<input type="hidden" name="_csrf" value="xxx"> -
AJAX / API 请求:优先放在
X-CSRF-Token请求头中;若框架限制,也可作为 JSON body 字段(如{"_csrf": "xxx", ...}) - 禁止拼入 URL 查询参数,防止泄露至 Referer、服务器日志或浏览器历史
- 不建议用 Cookie 自动携带(易与会话 Cookie 混淆,SameSite 不能替代校验)
服务端校验逻辑
校验必须在业务逻辑执行前完成,覆盖所有状态变更方法(POST/PUT/PATCH/DELETE),且严格按顺序提取和比对:
- 按优先级检查令牌来源:
X-CSRF-Token头 > 请求体(req.body._csrf) > 查询参数(req.query._csrf) - 从 session 或缓存中取出该用户的当前有效令牌,使用恒定时间比较函数(如
hash_equals()或crypto/subtle.ConstantTimeCompare)防时序攻击 - 校验失败立即返回
403 Forbidden,不执行后续操作,也不返回具体错误原因 - 成功后可选择一次性作废该令牌(推荐),或刷新其有效期(需配套刷新机制)
配套加固措施
令牌是主防线,但需叠加其他机制形成纵深防御:
- 设置会话 Cookie 的
SameSite=Lax(或 Strict),降低跨站自动携带风险 - 对高危操作(如转账、密码重置)增加二次确认或短时短信/邮箱验证码
- 辅助校验
Origin或Referer头(注意其不可靠性,仅作补充) - 明确禁止 GET 请求执行状态变更,从根本上规避部分 CSRF 场景











