CSRF攻击与防御详解
CSRF(跨站请求伪造) 是一种常见且危险的Web安全漏洞。
核心概念:
CSRF 攻击利用的是 Web 应用程序对用户浏览器的信任。攻击者诱骗受害者在已经通过目标网站(例如银行网站、社交网络、Web 邮件服务)身份验证的情况下,执行一个他们本无意执行的操作(如转账、更改密码、发布状态、发送邮件)。
攻击原理与流程:
- 用户登录: 受害者用户使用其凭据(用户名/密码)成功登录到一个可信赖的网站(例如
https://bank.com)。该网站验证用户身份后,会在用户的浏览器中设置一个会话 Cookie(例如SESSIONID=abc123)。这个 Cookie 标记了用户的登录状态。 - 保持登录状态: 只要用户不主动退出或会话未过期,浏览器在后续向
bank.com发出的每一个请求中,都会自动带上这个会话 Cookie。服务器通过验证这个 Cookie 来识别用户身份并授权操作。 - 受害者访问恶意网站: 在保持对
bank.com登录状态的同时,受害者被诱导访问了攻击者控制或植入恶意代码的网站(例如https://evil.com)。这种诱导方式可能是通过钓鱼邮件、社交媒体链接、恶意广告、甚至被篡改的正常网站等。 - 恶意请求构造:
https://evil.com的页面中包含精心构造的、指向目标网站(bank.com)的恶意请求。这个请求旨在执行一个敏感操作(例如,向攻击者账户转账)。请求的形式可以是:- 自动提交的隐藏表单:
<form action="https://bank.com/transfer" method="POST" style="display: none;"> <input type="hidden" name="amount" value="1000"> <input type="hidden" name="toAccount" value="ATTACKER_ACCOUNT_NUM"> <input type="submit" value="Click for free money!"> <!-- 用户可能看不到或不小心点击 --> </form> <script>document.forms[0].submit();</script> <!-- 自动提交 --> - 自动加载的图片/Script标签(常用于 GET 请求):
<img src="https://bank.com/transfer?amount=1000&toAccount=ATTACKER_ACCOUNT_NUM" width="0" height="0"> - AJAX 请求(受 CORS 限制,但某些简单请求或规避方式仍可能成功): 现代浏览器严格的 CORS 策略使得纯 AJAX CSRF 攻击难度增大,但并非完全不可能,尤其对于允许简单请求(GET/POST with certain headers/content-types)的端点。
- 自动提交的隐藏表单:
- 浏览器发送恶意请求: 当受害者访问
evil.com时,浏览器会加载并执行页面中的恶意代码。浏览器会向bank.com的/transfer端点发送一个请求(无论是表单提交还是图片加载)。 - 携带会话 Cookie: 关键点来了!因为受害者之前已经登录了
bank.com,并且会话 Cookie 仍然有效且属于bank.com域,浏览器在发送这个恶意请求时,会自动、静默地将用户的SESSIONID=abc123Cookie 附加到请求中。 - 服务器处理请求:
bank.com的服务器收到这个请求:- 它检查请求中的 Cookie:
SESSIONID=abc123。 - 它验证这个 Cookie 是有效的,对应一个已登录的用户(受害者)。
- 服务器认为这是一个来自合法用户的、经过授权的请求(因为它有有效的会话令牌)。
- 服务器执行请求指定的操作:将受害者的 1000 元转账到攻击者的账户。
- 它检查请求中的 Cookie:
- 攻击成功: 攻击者成功利用受害者的登录状态和浏览器的自动 Cookie 发送机制,在受害者不知情且未同意的情况下,执行了敏感操作。
CSRF 攻击成功的关键要素:
- 用户身份验证: 用户必须在目标站点上拥有活跃的登录会话(有效的 Cookie)。
- 基于 Cookie 的会话管理: 目标应用程序依赖 Cookie 来跟踪用户的会话状态。
- 无状态的 HTTP: HTTP 协议本身是无状态的,每个请求独立处理。服务器仅根据请求中包含的信息(如 Cookie)来判断请求者的身份和权限。
- 可预测的操作: 攻击者能够构造出执行敏感操作的请求(知道 URL、参数、方法)。
- 浏览器行为: 浏览器会在请求同源(或满足特定条件)的资源时,自动附加该源的所有有效 Cookie,无论请求的来源页面是哪里。这是核心机制。
CSRF 与 XSS 的区别:
虽然都是客户端攻击,但原理截然不同:
| 特性 | CSRF (跨站请求伪造) | XSS (跨站脚本攻击) |
|---|---|---|
| 目标 | 利用用户对目标站点的信任和登录状态。 | 破坏用户对目标站点的信任。 |
| 核心 | 伪造用户请求。 | 在目标站点注入并执行恶意脚本。 |
| 受害者 | 受害者浏览器代表用户执行操作。 | 受害者的浏览器执行恶意代码。 |
| 信任来源 | 服务器信任用户的浏览器发送的合法会话令牌。 | 用户的浏览器信任来自目标站点的脚本。 |
| 影响范围 | 执行用户在目标站点能执行的操作。 | 窃取 Cookie/会话、记录按键、DOM 操作等。 |
| 所需条件 | 用户已登录目标站点并访问恶意页面。 | 目标站点存在注入漏洞(输入未过滤/转义)。 |
防御措施:
防止 CSRF 的核心思路是让服务器能够区分哪些请求是用户真正有意发出的,哪些是伪造的。常用方法:
-
CSRF Tokens (最常用且最有效):
- 服务器在为用户生成表单(或页面)时,嵌入一个唯一的、不可预测的、与用户会话绑定的令牌(通常是一个长随机字符串)。这个令牌应该存储在服务器端(如 Session)并与用户会话关联。
- 当用户提交表单(或发起敏感请求,如 POST/PUT/DELETE)时,必须将这个 CSRF Token 包含在请求中(通常作为隐藏表单字段或自定义 HTTP Header,如
X-CSRF-Token)。 - 服务器在处理请求前,验证提交的 Token 是否与当前用户会话中存储的 Token 匹配,且有效。
- 为什么有效? 攻击者无法从
evil.com读取用户当前在bank.com页面上生成的 Token(受同源策略保护),因此无法在伪造的请求中包含正确的 Token。服务器会拒绝缺少或 Token 不匹配的请求。 - 关键点: 确保 Token 足够随机、绑定会话、安全传输(建议通过 POST body 或自定义 Header,避免放在 URL 中)、对每个敏感请求都验证。
-
SameSite Cookie Attribute (现代浏览器支持):
- 在设置会话 Cookie 时,指定
SameSite属性。 SameSite=Strict: 浏览器只会在请求 URL 与 Cookie 来源站点完全一致(同站)时才发送 Cookie。这意味着从evil.com发起的对bank.com的请求绝对不会携带bank.com的会话 Cookie。最严格,但可能影响跨站合法链接的用户体验(用户从邮件链接点回银行网站需要重新登录)。SameSite=Lax(现代浏览器的默认行为): 允许在安全的 HTTP 方法(如 GET)且是顶级导航(用户点击链接)时发送 Cookie 到跨站站点。但对于会改变状态的请求(如 POST),仍然只在同站请求中发送。这能阻止大多数 CSRF POST 攻击(如图片 GET 攻击可能仍存在,但危害通常较小)。- 为什么有效? 直接限制了浏览器在跨站请求中自动发送 Cookie 的行为,从根本上切断了 CSRF 攻击的依赖路径。
- 在设置会话 Cookie 时,指定
-
检查 Origin / Referer Header:
- 服务器在处理敏感请求时,检查 HTTP 请求头中的
Origin或Referer字段。 Origin指示了请求发起的源(协议+域名+端口)。Referer指示了请求来源页面的完整 URL。- 服务器验证这些 Header 的值是否与应用程序预期的合法源(自己的域名)匹配。如果不匹配或缺失,则拒绝请求。
- 局限性:
- 某些隐私设置或浏览器扩展会阻止发送
Referer。 Referer可能被篡改(尽管在 HTTPS 上下文中较难)。- 从 HTTPS 页面发起到 HTTP 端点的请求,浏览器通常不会发送
Referer。 - 不如 CSRF Token 可靠,通常作为深度防御的辅助手段。
- 某些隐私设置或浏览器扩展会阻止发送
- 服务器在处理敏感请求时,检查 HTTP 请求头中的
-
使用自定义请求头:
- 对于 AJAX 请求,可以在请求中添加一个自定义的 HTTP Header(例如
X-Requested-With: XMLHttpRequest)。 - 服务器检查请求中是否包含这个特定的 Header。
- 为什么有效? 受浏览器的同源策略限制,
evil.com上的脚本无法通过标准的XMLHttpRequest或fetchAPI 向bank.com发送带有自定义头的跨域请求(会触发 CORS 预检并被bank.com拒绝,除非bank.com显式允许evil.com的域发送该头,而这通常不会发生)。 - 局限性: 仅适用于 AJAX 调用。传统的表单提交或
<img>标签发起的 GET 请求无法添加自定义 Header。同样受 CORS 策略保护。
- 对于 AJAX 请求,可以在请求中添加一个自定义的 HTTP Header(例如
-
要求重新认证:
- 在执行极其敏感的操作(如转账、修改密码、更改邮箱)之前,强制要求用户重新输入密码或进行二次认证(如短信验证码)。
- 为什么有效? 即使 CSRF 攻击成功触发了操作请求,攻击者也无法知道用户的密码或控制用户的二次认证设备,因此无法完成最终的操作授权。
- 用户体验影响: 可能会降低用户体验流畅度,应权衡安全性和便利性,仅用于最高风险操作。
总结:
CSRF 是一种利用用户浏览器对目标网站的信任和自动发送 Cookie 机制的攻击。攻击者通过诱骗已登录用户访问恶意页面,让用户的浏览器在用户不知情的情况下向目标网站发送伪造的请求,执行恶意操作。
最有效和推荐的防御组合是:
- 对所有会改变状态的请求(POST, PUT, DELETE, PATCH)使用 CSRF Tokens。 这是黄金标准。
- 为会话 Cookie 设置
SameSite=Lax或SameSite=Strict属性。 利用现代浏览器的内置防护。 - (可选/辅助) 检查
Origin/RefererHeader 作为额外防护层。 - 对最高风险操作实施重新认证。

浙公网安备 33010602011771号