在 Web 安全领域,跨站请求伪造(Cross-Site Request Forgery, CSRF)是一种极为常见且危害巨大的攻击方式。尽管近年来随着现代框架的普及,CSRF 的防御手段已日趋成熟,但理解其底层原理与演变过程,仍然是每一位 Web 开发者与安全从业者的必修课。本文将从 CSRF 的核心原理出发,剖析常见利用方式,并系统梳理从传统到现代的防御策略。
一、什么是 CSRF?
CSRF 的本质是:攻击者诱导受害者在已登录目标站点的状态下,向目标站点发送一个非本意的请求。与 XSS(跨站脚本攻击)不同,CSRF 并不需要攻击者窃取用户的 Cookie 或会话令牌,而是直接“借用”用户浏览器中自动携带的凭证(如 Session Cookie、HTTP Basic Auth 等),以用户的名义执行操作。
一个经典的比喻是:你拿着自己的身份证去银行办理业务,银行只认身份证不认人。攻击者无法偷走你的身份证,但他可以骗你走进银行,让你在不知情的情况下签下一份转账单——因为银行看到你本人拿着身份证,便认为这是你的真实意愿。
二、CSRF 的攻击原理
CSRF 能够成立,通常需要同时满足以下三个条件:
- 用户已登录目标站点,浏览器中存有有效的会话 Cookie。
- 目标站点仅依赖 Cookie 进行身份认证,未对请求来源做额外校验。
- 攻击者能够构造出目标站点可接受的请求,并诱导用户触发。
当用户访问攻击者控制的页面时,该页面可以自动向目标站点发起请求。由于浏览器在同源策略下仍会为跨站请求携带目标站点的 Cookie(这是浏览器的默认行为),目标站点会误以为该请求来自用户本人,从而执行相应操作。
三、常见利用方式
1. GET 型 CSRF
这是最原始的形式。假设某银行网站存在如下接口:
GET https://bank.com/transfer?to=attacker&amount=10000
攻击者只需在恶意页面中嵌入一张图片:
<img src="https://bank.com/transfer?to=attacker&amount=10000" />
用户一旦访问该页面,浏览器就会自动发起请求,若用户已登录银行,转账便会在无声中完成。图片加载失败也无所谓,请求已经发出。
2. POST 型 CSRF
现代应用多使用 POST 处理敏感操作,但这并不能阻止 CSRF。攻击者可以构造一个自动提交的隐藏表单:
<form action="https://bank.com/transfer" method="POST" id="csrf-form">
<input type="hidden" name="to" value="attacker" />
<input type="hidden" name="amount" value="10000" />
</form>
<script>document.getElementById('csrf-form').submit();</script>
页面加载后表单自动提交,效果与 GET 型相同。
3. JSON 与 API 场景
对于接受 JSON 的 API,攻击者可能利用 text/plain 或表单编码绕过 Content-Type 检查。虽然跨域读取响应受同源策略限制,但“写操作”往往不需要读取响应即可生效,这使得 CSRF 在 API 场景中依然危险。
4. 结合 XSS 的复合攻击
若站点存在 XSS,CSRF 防御可能被彻底绕过,因为攻击者可以在同源上下文中直接读取 Token 并构造请求。因此,CSRF 与 XSS 常被视为一对“孪生威胁”,防御时需同时考虑。
四、传统防御手段及其局限
1. 验证 Referer / Origin
服务器检查请求头中的 Referer 或 Origin 是否来自本站。这种方法简单,但存在局限:部分用户或代理会剥离 Referer;某些隐私设置下 Origin 可能缺失;且规则配置不当易被绕过。
2. 双重 Cookie 验证
将随机值同时写入 Cookie 和请求参数,服务器比对两者是否一致。由于攻击者无法读取目标站点的 Cookie(受同源策略保护),无法在请求中伪造匹配的值。但若站点存在子域 Cookie 注入或 XSS,该防御可能失效。
3. CSRF Token
这是最经典的防御方式。服务器在表单或页面中嵌入一个随机、不可预测的 Token,并在提交时校验。Token 通常与用户会话绑定,且一次性或有时效性。攻击者因无法获知 Token 而无法构造有效请求。
然而,传统 Token 方案在多标签页、前后端分离、移动端 API 等场景下会带来复杂性,例如 Token 同步、刷新、跨域携带等问题。
五、现代防御策略
1. SameSite Cookie 属性
SameSite 是近年来最有效的 CSRF 防御机制之一。通过设置:
Set-Cookie: session=abc; SameSite=Lax; Secure; HttpOnly
SameSite=Strict:完全禁止跨站携带 Cookie,安全性最高,但可能影响从外部链接跳转的体验。SameSite=Lax:默认值(现代浏览器),允许顶级导航的 GET 请求携带 Cookie,但阻止跨站 POST、iframe、AJAX 等请求,能防御绝大多数 CSRF。SameSite=None:需配合Secure,用于需要跨站携带的场景(如嵌入式支付)。
SameSite 从浏览器层面切断了 CSRF 的根基,已成为现代 Web 应用的标配。
2. 自定义请求头校验
对于 AJAX API,要求请求必须携带自定义头(如 X-Requested-With: XMLHttpRequest 或自定义 Token 头)。由于浏览器同源策略限制,跨站请求无法自定义此类头,从而天然阻断 CSRF。这是前后端分离架构中常用的轻量方案。
3. 双提交 Cookie 的现代化
结合 SameSite 与签名 Cookie,或使用 __Host- 前缀 Cookie,可防止子域覆盖。服务器校验 Cookie 与请求体中的 Token 是否一致且签名有效,兼顾安全与无状态。
4. 框架内置防护
主流框架均已内置 CSRF 防护:
- Django:默认启用 CSRF 中间件,模板中需加
{% csrf_token %}。 - Spring Security:默认开启 CSRF 保护,可配置 Token 仓库。
- Laravel:通过
VerifyCsrfToken中间件自动校验。 - Ruby on Rails:
protect_from_forgery默认开启。
开发者应理解其原理,而非盲目依赖。
5. 纵深防御与零信任思路
现代安全实践强调纵深防御:
- 对敏感操作要求二次认证(如密码、OTP)。
- 使用短期、细粒度的 Token。
- 监控异常请求模式。
- 将 CSRF 防护与 CSP、XSS 防御结合,形成完整闭环。
六、总结
CSRF 虽是一个“古老”的漏洞类型,但其原理直击 Web 身份认证模型的软肋。随着 SameSite Cookie 的普及和框架内置防护的完善,CSRF 的攻击面已大幅收窄,但这并不意味着可以高枕无忧。开发者仍需理解其本质,根据业务场景选择合适的防御组合,并在 API 设计、Cookie 策略、前端交互等环节保持警惕。安全从来不是单一措施的结果,而是层层设防、持续演进的过程。
未经允许不得转载:任鹏个人博客 » CSRF 跨站请求伪造:原理、利用方式与现代防御策略

