最可靠的PHP CSRF防护是用session_regenerate_id(true)重置会话并存token于$_SESSION['csrf_token'],生成用bin2hex(random_bytes(32)),表单以htmlspecialchars处理的hidden字段传递,校验用hash_equals(),失效时静默拒绝。
PHP 中用
配合
存 token 最可靠
CSRF 的核心是攻击者能预测或复用你的请求凭证,所以关键不是“生成 token”,而是让 token 具备会话绑定、一次性、时效性。PHP 原生不自带 CSRF token 函数,得自己搭骨架。
不只是防 session fixation,它重置 session ID 的同时,也帮你清掉旧会话里可能被窃取的 token 状态。
每次用户登录或权限变更后必须调用
(
表示删除旧 session 文件)
token 存进
,别存 cookie 或前端 localStorage
生成用
,别用
这种可预测组合
表单提交后立即验证并重置:验证通过就
,防止重放
Form 提交时怎么传 token 才不被绕过
很多人把 token 放在 URL 参数里,或者藏在 JS 变量中,结果被 XSS 一抓一个准。真正安全的传法只有一种:作为隐藏字段,且和服务端存储的完全一致。
必须用
,
防止属性注入
不要用 JS 动态填充 value,避免 DOM XSS 污染值
如果页面有多个 form(比如编辑+删除按钮),每个都得用独立 token,或统一用同一个但校验后立即失效
AJAX 请求需从 DOM 读取 hidden 字段值,别从全局变量或 API 接口单独取——那等于暴露 token 获取通道
校验 token 是必须的,不是可选
直接用
或
比较 token 会触发时序攻击。攻击者靠响应时间差异就能暴力猜出 token 字符,尤其当 token 是 hex 字符串时更明显。
校验必须写成
两边都不能为空字符串,否则
会返回
,但你要先确保 key 存在,而不是让它崩在 undefined index
如果 token 已被 unset,直接拒绝,不给任何提示信息(连 HTTP 状态都别用 403,统一用 400 或重定向到错误页)
CSRF token 在多标签页或长页面中容易失效的几个现实问题
用户开两个 tab 同时操作,或者页面挂了半小时再提交,token 就大概率失效。这不是 bug,是设计使然,但体验要兜住。
PHP 8.5.5
PHP 8.5.5 是 PHP 8.5 分支的维护更新版本。该版本延续了“小步快跑”的迭代逻辑,通过深度错误修复、底层性能微调以及安全加固,旨在为开发者提供一个更健壮、更高效的运行环境。该版本严格遵守语义化版本规范,不包含破坏性变更。
下载
立即学习
“
PHP免费学习笔记(深入)
”;
session 生命周期别设太短(比如
是底线)
表单页加载时检查
是否存在,没有就现场生成一个并写入 session,别等提交才报错
AJAX 表单可加个前置请求
刷新 token,但该接口本身也要受 CSRF 保护(比如只允许 POST + referer 白名单)
别在 JS 里自动重试失败的提交——token 失效后重试只会反复失败,应提示用户刷新页面
最麻烦的其实是前后端分离场景:PHP 只管发 token,但前端可能缓存、复用、跨域丢 header。这时候 token 必须和 session ID 强绑定,且不能依赖客户端传来的 session_id cookie 做校验——得用服务端存储的 session 数据反查,否则换 cookie 就绕过了。
session_regenerate_id()$_SESSIONsession_regenerate_id()session_regenerate_id(true)true$_SESSION['csrf_token']bin2hex(random_bytes(32))md5(time().rand())unset($_SESSION['csrf_token'])htmlspecialchars()hash_equals()=====hash_equals($_SESSION['csrf_token'] ?? '', $_POST['csrf_token'] ?? '')hash_equals()falseini_set('session.gc_maxlifetime', 1440)$_SESSION['csrf_token']/api/csrf-token