PHP防CSRF核心是生成并校验会话级一次性令牌:调用session_start()后用bin2hex(random_bytes(32))生成唯一Token存入$_SESSION['csrf_token'],表单中以htmlspecialchars()转义后嵌入隐藏域;提交时用hash_equals()严格比对并立即unset()销毁,确保单次有效。

生成并绑定会话级CSRF令牌
每次用户开启新会话(如登录后),必须生成唯一、不可预测的令牌,并存入$_SESSION。PHP 8.x推荐使用random_bytes()而非mt_rand(),确保密码学安全性:
session_start();if (!isset($_SESSION['csrf_token'])) {$_SESSION['csrf_token'] = bin2hex(random_bytes(32));}
该令牌需与会话生命周期一致,不跨用户共享,也不在URL中传递。
在表单中安全嵌入隐藏字段
输出令牌时必须进行HTML上下文转义,防止被XSS污染或注入:
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token'], ENT_QUOTES, 'UTF-8'); ?>">
避免直接echo $_SESSION['csrf_token']——若会话数据曾被恶意写入(如通过其他漏洞),未转义将导致XSS链式攻击。
立即学习“PHP免费学习笔记(深入)”;
修复 Core bailout 状态损坏安全问题,以及 BZ2、DOM、PDO、Opcache/JIT 内存与执行错误,改进 Unicode 字素处理、URL 解析、TLS 流状态、信号分发及会话错误处理。
服务端严格校验并立即销毁
接收POST请求后,需同步验证令牌存在性、一致性及会话有效性,且验证通过后主动清除会话中的令牌,防止重放:
- 检查
$_POST['csrf_token']是否设置 - 用
hash_equals()比对(防时序攻击),而非=== - 确认
$_SESSION['csrf_token']仍存在且非空 - 校验通过后执行
unset($_SESSION['csrf_token'])
若表单支持多开(如多个编辑页同时打开),可改用“每表单独立令牌”策略,但需配合服务端令牌池管理,避免复杂度激增。
补充防护:SameSite Cookie + 关键操作二次确认
PHP 8.x环境应配合HTTP层加固:
- 设置Cookie的
SameSite=Lax(PHP 7.3+支持session_set_cookie_params(['samesite' => 'Lax'])),限制第三方上下文自动携带会话Cookie - 对敏感操作(如密码修改、资金转账、管理员权限变更)强制添加图形验证码或短信确认,绕过Token机制的潜在缺陷
单纯依赖Referer检查不可靠——部分浏览器可能不发送、代理可能剥离、内网环境常为空Referer。


















