跨站请求伪造 (CSRF)
1. 定义
跨站请求伪造 (Cross-Site Request Forgery, CSRF) 是一种强制用户在当前已登录的 Web 应用中执行非意愿操作的攻击。
它利用了浏览器的 “自动携带凭证(Ambient Authority)” 机制:基于目标域名,浏览器会自动在跨站请求中包含凭证(如 Session Cookie、Basic Auth 标头),而不管该请求的触发源头。
2. 技术原理
核心漏洞不在于服务器的身份验证逻辑,而在于其无法验证请求的真实意图。
当用户登录 bank.com 时,浏览器存储了会话 Cookie session_id=xyz。 如果用户随后访问了恶意网站 evil.com,且该网站包含以下代码:
<form action="https://bank.com/transfer" method="POST" id="hack">
<input type="hidden" name="amount" value="1000">
<input type="hidden" name="to" value="attacker">
</form>
<script>document.getElementById('hack').submit();</script>浏览器将执行此 POST 请求。关键点在于,浏览器会自动附加 session_id=xyz Cookie,因为目标地址是 bank.com。服务器收到有效的 Session Cookie 后处理了转账,却不知道请求实际上源自 evil.com。
3. 攻击流程 (序列图)
sequenceDiagram
participant Victim as 用户浏览器
participant Attacker as 恶意网站 (evil.com)
participant Server as 目标服务器 (bank.com)
Note over Victim, Server: 用户已登录 (Cookie: session=123)
Victim->>Attacker: 1. 访问恶意页面
Attacker-->>Victim: 2. 返回包含自动提交表单的页面
Note over Victim: 浏览器解析 HTML<br/>发现 <form action="bank.com">...
Victim->>Server: 3. 发送 POST /transfer (包含 Cookie: session=123)
Note right of Victim: 浏览器自动添加 Cookie<br/>Referer: https://evil.com
Server-->>Server: 4. 验证 Cookie (通过)<br/>5. 执行转账操作
Server-->>Victim: 6. 返回 200 OK (转账成功)4. 真实案例:Netflix (2006)
目标: Netflix “添加到租赁队列”功能。 漏洞类别: 基于 GET 的 CSRF。
2006 年,研究人员发现 Netflix 的“添加到队列”操作是通过简单的 GET 请求完成的: GET http://www.netflix.com/AddToQueue?movieid=70011204
攻击方式: 攻击者可以在网页中植入一个图片标签: <img src="http://www.netflix.com/AddToQueue?movieid=70011204" />
当已登录 Netflix 的用户加载攻击者的页面时:
- 浏览器尝试从 Netflix 获取“图片”。
- 请求中包含了用户活跃的 Netflix 认证 Cookie。
- Netflix 服务器处理了该 GET 请求,并将特定电影添加到了受害者的租赁队列中。
- 虽然这看起来影响较小,但它证明了可以在未经用户许可的情况下触发状态变更。
影响: 导致了大规模的用户数据操纵。这迫使 Netflix(以及整个行业)开始严格要求对状态变更操作使用 POST 方法,并实施抗 CSRF 令牌。
5. 深度防御策略
A. 同步令牌模式 (Synchronizer Token Pattern - 有状态)
这是最稳固的防御手段。
生成: 服务器为用户会话生成一个加密强度高、唯一且不可预测的随机令牌(CSRF Token)。
传输: 将此令牌作为隐藏字段嵌入到 HTML 表单中(或作为 AJAX 的特定 HTTP 标头发送)。
<input type="hidden" name="csrf_token" value="raNDomStr1ng...">验证: 在执行状态变更请求(POST、PUT、DELETE)时,服务器将提交的令牌与会话中存储的令牌进行比对。
- 如果令牌缺失或不匹配 -> 拒绝请求 (403 Forbidden)。
- 注意: 令牌不得存储在会被自动发送的 Cookie 中。
B. 双重提交 Cookie (Double Submit Cookie - 无状态)
适用于 REST API 等无状态后端。
- 服务器在 Cookie 中设置一个随机值(如
csrf_cookie),同时前端读取该值并将其放入自定义标头(如X-CSRF-Token)中发送。 - 服务器验证
Cookie.csrf_cookie === Header.X-CSRF-Token。 - 原理: 攻击者可以触发发送 Cookie 的请求,但受同源策略 (SOP) 限制,他们无法读取 Cookie 值来设置自定义标头。
C. SameSite Cookie 属性
浏览器层面的防御(深度防御)。 为会话 Cookie 设置 SameSite 属性:
SameSite=Strict:跨站请求从不发送 Cookie。(安全性最高,但影响用户体验)。SameSite=Lax:仅在安全的顶级导航(如 GET 链接)中发送 Cookie。(良好的平衡点)。
Set-Cookie: session_id=xyz; SameSite=Lax; Secure; HttpOnlyD. 标准标头验证 (Standard Headers Verification)
检查 Origin 和 Referer 标头。
- 如果存在
Origin,验证其是否匹配目标域名。 - 注意: 处于隐私原因,这些标头可能会被浏览器(或代理)移除或剥离,因此这应作为一种“深度防御”的辅助手段,而非主要防御手段。
