CSRF防护靠验证请求是否由用户主动发起,核心是使用一次性、随机、绑定用户且有时效的Token;攻击者利用浏览器自动携带Cookie的特性诱导用户发起伪造请求,服务器误判为合法操作。

CSRF不是靠“拦截请求”来防,而是靠“验证请求是否真由用户主动发起”。核心是给每次敏感操作发一张一次性入场券(Token),服务器核对无误才执行——这张券必须随机、绑定当前用户、有时效、用完即作废。
CSRF是怎么骗过服务器的
用户登录后,浏览器自动携带 Cookie 访问任何页面。攻击者诱导用户点击一个恶意链接(比如伪装成图片或跳转页),该链接向你的网站发起 POST 请求(如转账、删账号)。因为 Cookie 还在,服务器以为是用户自己点的,就照常执行了——整个过程用户毫无感知。
关键点在于:CSRF 不窃取 Cookie,也不需要知道密码;它只利用浏览器“自动带凭证”的默认行为,伪造一次合法请求。
生成安全 Token 的硬性要求
Token 不是随便拼个字符串就行,必须满足三个条件:不可预测、一次有效、绑定上下文。
立即学习“PHP免费学习笔记(深入)”;
- ✅ 用 random_bytes() 生成(PHP 7.0+):
$token = bin2hex(random_bytes(32)); - ❌ 禁用
md5(time().rand())、uniqid().mt_rand()等弱随机源——时间戳和伪随机数可被爆破 - Token 存入 Session 时建议结构化,至少包含:
• 当前 IP 的哈希(hash('sha256', $_SERVER['REMOTE_ADDR'] ?? ''))
• 创建时间(time())
• Token 值本身
表单嵌入与提交校验全流程
以修改邮箱为例,前后端需配合完成闭环:
-
生成并嵌入(display.php):
session_start();<br> if (!isset($_SESSION['csrf_token']) || time() - ($_SESSION['csrf_token']['created_at'] ?? 0) > 3600) {<br> $_SESSION['csrf_token'] = [<br> 'value' => bin2hex(random_bytes(32)),<br> 'created_at' => time(),<br> 'ip_hash' => hash('sha256', $_SERVER['REMOTE_ADDR'] ?? '')<br> ];<br> }<br> <form method="post"><br> <input type="hidden" name="token" value="<?= htmlspecialchars($_SESSION['csrf_token']['value']) ?>"><br> ...其他字段<br> </form> -
提交时校验(update.php):
session_start();<br> $valid = isset($_POST['token'])<br> && isset($_SESSION['csrf_token'])<br> && hash_equals($_SESSION['csrf_token']['value'], $_POST['token'])<br> && (time() - $_SESSION['csrf_token']['created_at'] <= 3600)<br> && hash_equals($_SESSION['csrf_token']['ip_hash'], hash('sha256', $_SERVER['REMOTE_ADDR'] ?? ''));<br> if (!$valid) { die('Invalid or expired CSRF token'); }<br> // 校验通过后立即销毁<br> unset($_SESSION['csrf_token']);
AJAX 和多标签页场景怎么处理
纯表单隐藏域只够用在传统页面提交。一旦引入 JS 提交,必须统一注入机制,否则防护断层。
- AJAX 请求应在请求头中传 Token:
fetch('/api/update-email', {<br> method: 'POST',<br> headers: {'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]').content},<br> body: JSON.stringify({...})<br> });
后端从$_SERVER['HTTP_X_CSRF_TOKEN']读取并校验 - 支持多标签页?别用单 Token,改用“令牌池”:
• Session 中存最近 3 个有效 Token 数组
• 每次新生成 Token 时array_unshift()入栈
• 校验成功后用array_splice()清除已匹配项
别漏掉这些基础加固点
CSRF 防护不是加个 Token 就高枕无忧,还要堵住其他路径漏洞:
- 敏感操作一律禁用 GET,例如
/delete?id=123必须改为 POST 表单或带 Token 的 API - 设置 Session Cookie 的 SameSite 属性:
session_set_cookie_params(['samesite' => 'Lax']);(PHP 7.3+) - Token 绝不通过 URL 传递(会泄露在 Referer、日志、代理缓存中)
- 登出或 Session 销毁时,务必
unset($_SESSION['csrf_token']) - 前端输出 Token 一定要
htmlspecialchars(),防止 XSS 泄露 Token



















