文章目录
- 一、用一句话讲透原理
- 二、攻击长什么样(认识即可)
- 1. 经典:自动提交的表单
- 2. 图片 / 链接触发的 GET
- 3. JSON 接口就一定安全吗?
- 4. 登录 CSRF 与其它变体
- 三、防御总纲:你要证明“这是用户本人意图”
- 四、CSRF Token:仍然是基本功
- 1. 同步器令牌模式
- 2. 双提交 Cookie(Double Submit)
- 3. 框架优先
- 五、Origin / Referer 校验
- 六、SameSite Cookie:现代浏览器的“半自动防线”
- 1. SameSite 是干什么的
- 2. Lax 为什么常常“够用却不够”
- 3. Strict 的代价
- 4. None 用在什么地方
- 5. 和“站”的定义有关
- 6. SameSite 解决不了什么
- 七、推荐的组合策略(2020 年代务实版)
- 对外 Web 应用
- 纯 API + Bearer Token(前端存内存)
- Cookie 会话的 SPA
- 第三方支付回调 / Webhook
- 八、授权测试怎么测 CSRF
- 九、常见翻车现场
- 十、收尾
- 附:SameSite 选择速查
做安全的人里,有一批人对 CSRF 的第一印象是:“不就是表单里加个 token 吗?”
做业务的人里,另一批人的第一印象是:“我们全是 JSON 接口,好像不太怕。”
两边都只对了一半。CSRF(Cross-Site Request Forgery,跨站请求伪造)的核心从来不复杂——浏览器会在跨站请求里自动带上目标站的 Cookie(在策略允许时)——可一旦碰上 SameSite 默认值变化、前后端分离、移动 WebView、文件上传、GET 误伤业务,坑就会一个个冒出来。
一、用一句话讲透原理
你已经登录了银行站点bank.example,浏览器里躺着会话 Cookie。
你另开一个标签,访问了恶意页evil.example。恶意页可以让你的浏览器向bank.example发起一个请求——比如“转账”。
关键在于:在很多历史默认行为下,浏览器发这个请求时,会自动附带bank.example的 Cookie。服务器若只靠“有没有合法会话 Cookie”判断“是不是用户本人意愿”,就会把跨站伪造的请求当成用户点的。
所以 CSRF 骗的不是密码,是服务器对浏览器自动凭证的信任。
和 XSS 对比一下更好记:
| XSS | CSRF | |
|---|---|---|
| 核心 | 在目标站上下文执行攻击者脚本 | 借用用户浏览器向目标站发请求 |
| 要不要目标站有漏洞页 | 通常要有注入点 | 要有“只认 Cookie、不认意愿”的接口 |
| 典型结果 | 偷会话、改页面、继续作恶 | 以用户身份完成状态变更 |
XSS 有时能直接做完 CSRF 能做的事;但没有 XSS 时,CSRF 仍可能单独成立。两者都要防。
二、攻击长什么样(认识即可)
1. 经典:自动提交的表单
恶意页里放一个自动提交到目标站的表单(POST),用户一旦打开恶意页,浏览器就带着 Cookie 把请求打出去。用户可能只看到页面闪一下,账户状态却变了。
2. 图片 / 链接触发的 GET
若危险操作竟然用 GET 就能完成(例如GET /delete?id=1),那一张<img src="...">都可能触发。
这首先是业务接口设计错误:改变状态的操作不该是 GET。即便防了 CSRF,也应改成 POST/PUT/DELETE + 鉴权。
3. JSON 接口就一定安全吗?
不完全。确实,简单 HTML 表单很难发Content-Type: application/json的复杂请求;浏览器的 CORS 预检也会挡一批“跨域读响应”。但 CSRF 关心的往往是请求有没有副作用,不是攻击者能不能读返回值。
仍需小心:
- 服务端若接受
text/plain等方式“擦边”解析出 JSON; - 某些接口其实也吃表单编码;
- 同站点子域被拿下后的“跨站”边界更细;
- 配合 XSS / 子域接管时,SameSite 与 CORS 模型会更复杂。
结论:前后端分离 ≠ 自动免疫 CSRF,要看 Cookie 怎么带、接口怎么鉴权。
4. 登录 CSRF 与其它变体
还有“强迫用户登录成攻击者账号”一类登录 CSRF,用于后续投毒或追踪。是否划进你们的威胁模型,取决于业务。支付、改密、授权绑定、关闭 MFA 等,一律按高危状态变更保护。
三、防御总纲:你要证明“这是用户本人意图”
会话 Cookie 证明“浏览器里有谁的登录态”;
CSRF 防御要额外证明“这次请求是用户在本站上下文里发起的”。
常见积木:
- CSRF Token(同步器令牌 / 双提交 Cookie 等)
- 检查 Origin / Referer
- SameSite Cookie
- 不用 Cookie 做会话(如 Authorization Bearer,注意仍有别的风险)
- 关键操作二次确认 / 重新输入密码 / MFA
- Get 无副作用
实战里很少只靠一样。SameSite 大幅降低了成本,但不是万能符。
四、CSRF Token:仍然是基本功
1. 同步器令牌模式
服务端给每个会话(或每次表单)下发随机 token,存在服务端会话里。
浏览器提交状态变更时必须带上这个 token(表单隐藏域或自定义头)。
服务端校验 token 与会话绑定一致。
攻击者的站点读不到你的页面内容(受同源策略限制),也就拿不到 token——于是跨站请求缺令牌,被拒。
要点:
- token 要不可预测、足够长;
- 验证失败要拒绝,不要“没带也行”;
- 注意刷新、多标签页、缓存页面导致的 token 过期体验问题;
- SPA 常见做法:从 cookie 读 token 再复制到 Header(见双提交),或由后端接口下发到前端内存。
2. 双提交 Cookie(Double Submit)
服务端设一个 CSRF Cookie,前端 JS 读取后放到请求头(如X-CSRF-Token)。服务端比较 Cookie 与 Header 是否一致。
它依赖:攻击者不能轻易读写你的 Cookie,也不能在跨站场景下方便地设置你的域 Cookie 并同时控制头(结合现代浏览器策略)。实现细节一错(例如 Cookie 过于宽松、子域可写)就会翻车。框架自带实现通常比手搓安全。
3. 框架优先
Django、Rails、Spring Security、ASP.NET、Laravel 等几乎都有现成 CSRF 中间件。
优先用框架,别在业务里东一块西一块。
五、Origin / Referer 校验
服务端检查Origin或Referer是否为本站可信来源。跨站表单请求往往会带上恶意源,或在某些情况下缺失。
优点:实现相对简单,对传统表单 CSRF 有效。
缺点与坑:
- Referer 可能被隐私策略、浏览器设置裁掉;
- 误杀:合法无 Referer 的客户端;
- 只靠 Referer 不如 Origin 清晰;
- 需维护可信域列表(含多域名站点)。
实操建议:能拿 Origin 先拿 Origin;缺失时策略要明确(拒绝或走额外校验);与 token 组合更稳。
六、SameSite Cookie:现代浏览器的“半自动防线”
1. SameSite 是干什么的
Cookie 的SameSite属性控制:在跨站请求中是否携带该 Cookie。
常见值:
| 值 | 含义(直观理解) |
|---|---|
Strict | 跨站请求基本不带这个 Cookie;最严 |
Lax | 跨站“导航式”GET 等部分场景可能带;常见跨站 POST 不带 |
None | 跨站也可带;但通常要求同时Secure(仅 HTTPS) |
浏览器近几年把默认策略收紧(许多场景默认按 Lax 思路处理未声明的 Cookie,具体以浏览器为准)。这让大量“裸 POST + 会话 Cookie”的老式 CSRF自然失效了一大截——好事,但别据此拆掉服务端校验。
2. Lax 为什么常常“够用却不够”
Lax下,用户从外站点开一个链接(顶级导航 GET)进入你的站时,仍可能带上 Cookie——这是为了体验(外链进站保持登录)。
因此:若你的危险操作可用 GET 完成,Lax 挡不住。
再次强调:状态变更用 POST 等,并配合 token。
3. Strict 的代价
更安全,但用户从外部链接点进网站可能呈现未登录,体验受损。高安全后台、管理端可以上 Strict;对 C 端大站,更多用 Lax + CSRF token。
4. None 用在什么地方
跨站需要带 Cookie 的场景:第三方嵌入、某些 SSO、跨站 API(本身就很敏感)。
必须Secure,必须 HTTPS,必须清楚自己在扩大 CSRF 攻击面——此时更要有 token 或其它强校验,不能只靠 SameSite。
5. 和“站”的定义有关
SameSite 里的“站(site)”按 eTLD+1 等规则理解,和“源(origin)”不完全一样。a.example.com与b.example.com在 SameSite 看来可能是同站,但在同源策略上不同源。
子域接管、兄弟子域 XSS,会让“同站 Cookie”模型更危险。子域安全与 Cookie 域范围(Domain=)要一起设计。
6. SameSite 解决不了什么
- 同站内的请求伪造(子域恶意页面打主域接口);
- 非 Cookie 凭证方案下的其它伪造;
- 用户已被 XSS 的情况;
SameSite=None的跨站带 Cookie 场景;- GET 副作用接口在 Lax 下的风险。
所以官方与业界共识接近:SameSite 是显著缓解,不是完整替代 CSRF token。
七、推荐的组合策略(2020 年代务实版)
对外 Web 应用
- 会话 Cookie:
Secure+HttpOnly+SameSite=Lax(或管理端 Strict); - 所有状态变更:CSRF token 或框架等价物;
- 校验 Origin(可作附加层);
- GET/HEAD 无副作用;
- 改密、转账、授权:二次验证。
纯 API + Bearer Token(前端存内存)
不自动带 Cookie,天然规避经典 CSRF。但要注意 XSS 偷 token、token 存储位置、刷新令牌泄漏。不是“更安全的免费午餐”,是换威胁模型。
Cookie 会话的 SPA
依然按 Cookie 场景防 CSRF:双提交或同步器令牌;别假设 JSON 万能。
第三方支付回调 / Webhook
那是服务端到服务端,应用签名校验,不走浏览器 CSRF 模型。别和浏览器 CSRF 混为一谈。
八、授权测试怎么测 CSRF
- 登录受害者账号,抓一条状态变更请求;
- 去掉 token / 改坏 token,看是否仍成功——应失败;
- 用跨站 HTML 复现(靶场),观察 Cookie 是否带上、结果是否变更;
- 检查 Cookie 的 SameSite / Secure / HttpOnly;
- 扫一遍危险 GET;
- 子域、多域名、跨站 iframe 场景单独评估。
报告里写清:接口、方法、缺了哪层防护、SameSite 现状、修复建议。
不要在真实用户账户上做破坏性演示。
九、常见翻车现场
“我们加了 token,但有的接口忘了。”
全局中间件白名单要极短,且审计。
“Token 放在 Cookie 里,又没校验头,等于没防。”
“全站 SameSite=None 为了嵌入。”
等于主动回到跨站带 Cookie 时代,必须强 token。
“Referer 全拦,APP 内 WebView 误伤。”
要区分客户端,用组合策略。
“有 XSS 还纠结 CSRF。”
先修 XSS;XSS 在时可直接操作用户会话。
“跨域 CORS 配了 Access-Control-Allow-Origin: * 还带凭证。”
这是灾难级配置。带 Cookie 的 CORS 必须精确源,绝不能*。
十、收尾
CSRF 的原理薄如纸:浏览器自动递凭证,服务器误当成用户点头。
防住它的思路也稳定:再要一个“只有本站页面才拿得到的证据”——token、严格的源校验,再加上 SameSite 这道浏览器给的缓冲。
SameSite 让今天的默认世界比十年前安全,但它不会替你改掉危险的 GET,不会替你看好SameSite=None,也不会替你防守子域和 XSS。把它当保险杠,把 token 与正确的会话设计当刹车。
今晚若只做一件事:打开浏览器开发者工具,看你们主站会话 Cookie 有没有SameSite和Secure;再随便抓一个改资料/下单请求,看有没有 CSRF token 或等价头。
两眼看完,你就知道自己站在“靠浏览器默默挡着”,还是“纵深真的做了”。
附:SameSite 选择速查
| 场景 | 更常见选择 |
|---|---|
| 普通 C 端站点会话 | Lax+ CSRF token |
| 高安全后台 | Strict+ token |
| 必须跨站带 Cookie | None; Secure+ 强 token + 极小范围 |
| 无 Cookie 会话 API | Bearer 等 + 防 XSS |