web学习笔记编程技术学习与分享

CSRF 的本质与 Token 校验

2026 年 08 月 16 日约 3 分钟阅读 网络与安全

CSRF(跨站请求伪造)的攻击面只有一个来源:浏览器在跨站请求里也会自动携带 Cookie。理解这一点,所有防御手段都好解释。

攻击的样子

用户登录了银行网站 A,Cookie 还在有效期内。此时访问恶意页面 B,B 里藏着:

<img src="https://bank.example/transfer?to=hacker&amount=1000" style="display:none">

浏览器向 A 发起 GET 请求,自动带上 A 的 Cookie。如果 A 的转账接口接受 GET 且不校验来源,请求成功。

POST 也能伪造:一个自动提交的 form 跨站指向 A。只有 JSON + 自定义头的请求相对安全——跨站表单发不出自定义头。

setcookie('sid', $v, ['samesite' => 'Lax', 'secure' => true, 'httponly' => true]);
  • Lax:跨站 GET 导航带 Cookie,POST 不带——挡住绝大多数 CSRF,兼容性好;
  • Strict:跨站一律不带,最安全但「从外链点进来未登录」体验受损;
  • None:必须配 Secure,用于需要跨站的场景(SSO 回跳)。

现代浏览器已默认 Lax,但这不能作为唯一防线——老浏览器和非标准客户端仍需服务端校验。

防御二:CSRF Token

在表单里放一个服务端签发的随机值,提交时校验。攻击者读不到你的页面内容(同源策略),伪造不出这个 token:

$_SESSION['_csrf'] = bin2hex(random_bytes(16));
// 表单
<input type="hidden" name="_csrf" value="<?= htmlspecialchars($_SESSION['_csrf']) ?>">
// 校验
if (!hash_equals($_SESSION['_csrf'], $_POST['_csrf'] ?? '')) fail();

要点:与 Session 绑定(不与 Cookie 独立就失去意义)、用 hash_equals 防时序比较、每个会话一份即可(per-request 会破坏后退按钮)。

AJAX 场景把 token 放自定义头 X-CSRF-Token,服务端对写操作校验。

防御三:确认关键操作

转账、改密码这类操作,二次确认(密码/验证码)同时防 CSRF 和会话劫持,值得为高风险动作保留。

三层按序部署:SameSite 是底线(配置一次永久生效),Token 是主力,二次确认保护高危操作。测试用 OWASP ZAP 或手写一个带自动提交表单的本地页面,验证写接口是否全部拒绝跨站。

CSRF安全Cookie