目录
1、概念
跨站点请求伪造(Cross-Site Request Forgery,CSRF):攻击者用来通过用户浏览器冒充用户身份向服务器发送伪造请求并被目标服务器成功执行的漏洞。
简单来说,就是攻击者利用用户当前在某个网站(如银行、社交媒体、邮箱)的登录状态(即浏览器的会话 Cookie 或其他身份验证凭证),在用户不知情或未授权的情况下,以该用户的身份执行恶意操作。
2、攻击原理与过程
-
用户登录: 用户使用浏览器登录到一个受信任的网站(例如
https://bank.com
)。登录成功后,该网站会在用户的浏览器中设置一个会话 Cookie(例如SessionID=abc123
),用于标识该用户已通过身份验证。 -
用户访问恶意网站: 用户在保持
bank.com
登录状态的同时(会话 Cookie 仍然有效),访问了另一个由攻击者控制的网站(例如https://evil.com
)。这可能发生在用户点击了钓鱼邮件中的链接、访问了一个被黑的正常网站,或者浏览了包含恶意广告的页面时。 -
恶意请求触发:
evil.com
的页面包含精心设计的代码(通常是隐藏的表单、自动提交的脚本、<img>
标签、<iframe>
等),这些代码会向bank.com
发起一个请求。这个请求旨在执行一个敏感操作(例如转账、修改密码、更改邮箱地址、发表评论、购买商品)。-
关键点 1:伪造请求。 这个请求是攻击者伪造的,它模拟了用户本人在
bank.com
上执行该操作时浏览器会发出的请求(相同的 URL、方法 GET/POST/PUT/DELETE、参数)。 -
关键点 2:浏览器自动发送凭证。 由于用户已经登录了
bank.com
,浏览器在向bank.com
域发送请求时,会自动带上该域下的会话 Cookie (SessionID=abc123
)。
-
-
服务器处理请求:
bank.com
的服务器收到这个请求。-
它看到请求中包含了有效的会话 Cookie (
SessionID=abc123
)。 -
服务器验证 Cookie 有效,确认这是来自“已登录用户”的请求。
-
服务器没有(或者没有有效的方式)区分这个请求是用户主动在
bank.com
界面上发起的,还是由evil.com
上的恶意代码偷偷发起的。 -
服务器认为这是一个合法的用户请求,并执行该操作(例如,将钱转到攻击者的账户)。
-
-
攻击完成: 恶意操作在用户不知情的情况下被执行,用户可能直到事后(如收到转账通知)才会发现。
3、攻击场景示例
3.1 GET请求攻击(较少)
-
假设银行转账可以通过 GET 请求完成:
https://bank.com/transfer?to=attacker&amount=1000
-
攻击者在
evil.com
上放置一个图片标签:<img src="https://bank.com/transfer?to=attacker&amount=1000" width="0" height="0">
-
用户访问
evil.com
时,浏览器会自动加载该图片,向银行发送 GET 请求。如果用户已登录且银行不检查来源,转账就发生了。
3.2 POST 请求攻击 (最常见)
-
攻击者在
evil.com
上创建一个隐藏表单:<form action="https://bank.com/transfer" method="POST" id="maliciousForm" style="display:none;"> <input type="hidden" name="to" value="attacker" /> <input type="hidden" name="amount" value="1000" /> </form> <script> document.getElementById('maliciousForm').submit(); </script>
-
用户访问该页面,脚本自动提交表单,浏览器向
bank.com
发送 POST 请求,携带会话 Cookie。
3.3 AJAX 攻击 (需绕过 CORS)
-
现代应用常用 AJAX。如果目标站点配置的 CORS 策略过于宽松(如允许
evil.com
的源),攻击者可以使用 JavaScript 直接发送 AJAX 请求。 -
即使 CORS 策略严格,攻击者仍然可以发送“简单请求”(如 GET、特定 Content-Type 的 POST)或使用表单提交模拟 AJAX 行为(因为表单提交不受 CORS 同源策略限制)。
4、CSRF 攻击成功的关键要素
-
用户已登录目标站点: 目标站点存在有效的用户会话。
-
目标站点仅依赖 Cookie 等浏览器自动发送的凭证进行身份验证: 服务器只认 Cookie,不要求额外的、攻击者无法预测或获取的凭证(如 CSRF Token)。
-
目标操作可通过请求(GET/POST/PUT/DELETE)触发: 存在一个执行敏感操作的 API 端点。
-
攻击者能预测或构造出该操作的请求细节: URL、参数、方法等。
-
用户访问了包含恶意代码的页面: 攻击者有办法让已登录用户访问到攻击页面。
5、危害分析
-
在用户不知情的情况下进行资金转账。
-
更改用户密码、邮箱地址、安全设置。
-
以用户身份发布内容、发送消息、进行购买。
-
泄露用户敏感信息(如果请求能触发返回数据)。
-
破坏用户账户或服务。
6、CSRF 防御措施
6.1 使用 CSRF Tokens (最主流、最有效)
-
服务器在渲染表单或页面时,生成一个唯一的、不可预测的、与当前用户会话绑定的令牌(Token)。
-
该令牌作为隐藏字段包含在表单中,或者嵌入在页面的元数据(如
<meta>
标签)中供 JavaScript 读取。 -
当用户提交表单或发起敏感请求(特别是非 GET 请求)时,必须将这个 CSRF Token 随请求一起发送回服务器(可以放在表单数据、HTTP 头如
X-CSRF-Token
中)。 -
服务器收到请求后,验证请求中的 Token 是否与会话中存储的 Token 匹配且有效。
-
为什么有效? 攻击者无法从
evil.com
读取用户当前在bank.com
页面上的 CSRF Token(受同源策略保护),因此无法在伪造的请求中包含正确的 Token。服务器验证失败,拒绝执行操作。
6.2 使用 SameSite Cookie 属性
-
在设置会话 Cookie 时,添加
SameSite
属性。 -
SameSite=Strict
:最严格,浏览器只会在用户直接从目标站点导航(即 URL 栏输入或点击目标站点链接)时发送 Cookie。来自跨站点的请求(如图片、表单、AJAX)绝不发送 Cookie。安全性最高,但可能影响部分跨站点的合法用户体验(如从邮件链接点回站点)。 -
SameSite=Lax
:默认推荐值。允许在安全的顶级导航(如点击链接)和 GET 请求中发送 Cookie,但阻止在跨站点 POST 请求、<iframe>
嵌入、图片、AJAX 等场景中发送 Cookie。能有效防御大多数 CSRF 攻击(特别是基于 POST 的),同时保持较好的用户体验。 -
SameSite=None
:Cookie 可以在任何上下文中发送,但必须同时设置Secure
属性(仅限 HTTPS)。这是最宽松的设置,不提供 CSRF 防护,仅在需要跨站点共享 Cookie 的特定场景下使用(通常需要结合其他防御措施)。 -
为什么有效? 它限制了浏览器在跨站点请求中自动发送 Cookie 的行为,使得攻击者伪造的请求无法携带关键的会话 Cookie。
6.3 双重提交验证
-
原理与 CSRF Token 类似,但 Token 同时存储在 Cookie 和表单中(或请求头中)。
-
服务器验证请求中提交的 Token 是否与 Cookie 中携带的 Token 值一致。
-
攻击者无法读取或修改目标站点的 Cookie(受同源策略保护),因此无法让两者匹配。
6.4 检查 Referer/Origin 头
-
服务器在处理敏感请求时,检查 HTTP 请求头中的
Origin
或Referer
字段。 -
Origin
头通常只存在于跨域请求中,指示发起请求的源(协议+域名+端口)。 -
Referer
头指示请求来源页面的完整 URL。 -
服务器验证该头部的值是否来自自己的合法域名(
https://bank.com
)。 -
局限性:
-
隐私设置或代理可能阻止浏览器发送这些头。
-
Referer
可能被篡改(虽然浏览器本身会保护它)。 -
一些旧的或配置不当的浏览器/客户端可能不发送。
-
通常作为辅助防御手段,不能单独依赖。
-
6.5 要求用户交互/二次确认
-
对于关键操作(如转账、改密码),在执行前要求用户输入密码、短信验证码、或进行其他形式的二次确认。
-
这虽然不是纯粹的 CSRF 防御,但增加了攻击难度,因为攻击者无法诱导用户执行这些交互。
7、现代框架与最佳实践
-
大多数现代 Web 框架(如 Django, Rails, Spring Security, ASP.NET Core, Express with csurf/csurf替代库等)都内置了 CSRF Token 防御机制,开发者应启用并正确配置它们。
-
强烈推荐组合使用
SameSite=Lax
/Strict
Cookie 属性和 CSRF Token。 前者提供了浏览器级别的防护基础,后者提供了应用级别的深度防御。这是当前最健壮的防御组合。 -
避免使用 GET 请求执行状态修改操作。遵循 RESTful 原则(GET 用于读取,POST/PUT/DELETE 用于修改)。
8、CSRF漏洞测试工具:CSRFTester
核心原理:通过本地代理(默认端口8008)捕获浏览器所有HTTP请求及表单数据,允许修改参数后重新提交(相当于一次伪造客户端请求),验证服务器是否接受伪造请求。此工具也可以被用来进行CSRF攻击,
操作流程:
-
启动工具并设置浏览器代理指向
127.0.0.1:8008
; -
点击
Start Recording
录制用户操作(如登录、修改信息); -
修改表单内容(如账户名、转账金额);
-
生成HTML格式的PoC页面并触发提交,观察服务器响应
典型场景:快速验证表单类操作的CSRF漏洞,尤其适合传统HTML表单提交的Web应用。
局限:对JSON/API类请求支持较弱,误报率较高需人工复核