CSRF(跨站请求伪造)
威胁描述
CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种攻击方式,攻击者通过欺骗用户在已认证的Web应用程序上执行非预期的操作。由于浏览器会自动携带用户的认证凭证(如Cookie),Web应用程序无法区分请求是来自用户主动操作还是来自攻击者的诱导。
工作原理
- 用户登录信任的网站A,并获得认证Cookie
- 攻击者诱导用户访问恶意网站B
- 恶意网站B向网站A发送一个请求,执行攻击者预设的操作(如转账、修改密码等)
- 浏览器自动携带用户在网站A的认证Cookie
- 网站A验证请求中的Cookie,认为是用户主动发起的请求,从而执行该操作
示例代码
攻击页面示例
<!DOCTYPE html>
<html>
<head>
<title>CSRF攻击示例</title>
</head>
<body>
<h1>免费礼品!</h1>
<p>点击下方按钮领取免费礼品</p>
<!-- 隐藏的表单,会自动提交到目标网站 -->
<form id="csrf-form" action="https://bank.example.com/transfer" method="POST">
<input type="hidden" name="toAccount" value="attacker123">
<input type="hidden" name="amount" value="1000">
<button type="submit">立即领取</button>
</form>
</body>
</html>
基于图片的CSRF攻击
<!-- 当用户加载此页面时,会自动发送GET请求 -->
<img src="https://bank.example.com/transfer?toAccount=attacker123&amount=1000" width="0" height="0">
防御措施
1. CSRF令牌
服务器为每个用户会话生成一个唯一的令牌,并将其嵌入到页面中或存储在Cookie中。当用户提交表单时,需要同时提交这个令牌,服务器验证令牌的有效性:
<!-- 表单中的CSRF令牌 -->
<form action="/transfer" method="POST">
<input type="hidden" name="csrfToken" value="<?= $csrfToken ?>">
<!-- 其他表单字段 -->
</form>
2. SameSite Cookie属性
设置Cookie的SameSite属性,限制Cookie仅在同一站点请求中发送:
Set-Cookie: sessionId=abc123; SameSite=Strict
或
Set-Cookie: sessionId=abc123; SameSite=Lax
3. 双重提交防护
同时在Cookie和请求参数中发送CSRF令牌,并验证两者是否匹配:
// 前端获取Cookie中的CSRF令牌并添加到请求头
const csrfToken = document.cookie.replace(/(?:^|;\s*)XSRF-TOKEN=([^;]*)/, '$1');
fetch('/api/transfer', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-XSRF-TOKEN': csrfToken
},
body: JSON.stringify({ /* 请求数据 */ })
});
4. 验证Referer头部
服务器验证请求的Referer头部,确保请求来自合法的源:
Referer: https://example.com/transfer-page
最佳实践
-
对所有状态改变的请求实施CSRF保护,而不仅仅是敏感操作
- 即使是看似不敏感的操作(如更改用户头像、更新个人资料)也可能被攻击者利用。例如,攻击者可能通过更改用户的联系方式来绕过后续的安全验证。
- 一致的保护策略可以避免因选择性实施而产生的安全漏洞。
-
优先使用CSRF令牌结合SameSite Cookie属性的双重防护
- CSRF令牌提供主动验证机制,确保请求来自合法用户的主动操作。
- SameSite Cookie提供被动防御,限制Cookie仅在同一站点请求中发送,有效阻止跨站请求携带认证凭证。
- 双重防护可以应对各种复杂的攻击场景,即使一种防御措施失效,另一种仍能提供保护。
-
避免使用GET请求执行状态改变操作
- GET请求容易被攻击者通过图片标签、链接、iframe等方式诱导用户发送。
- GET请求会被浏览器缓存,增加了攻击的持久性风险。
- 遵循HTTP规范,GET请求应仅用于获取数据,而POST、PUT、DELETE等请求应用于修改数据。
-
对于API接口,使用令牌认证(如JWT)而非仅依赖Cookie
- API接口通常被各种客户端(如移动应用、第三方服务)调用,令牌认证更加灵活。
- 将令牌放在请求头中而非Cookie中,可以避免基于Cookie的CSRF攻击。
- JWT等令牌可以包含更丰富的权限信息,实现更细粒度的访问控制。
-
定期进行安全测试,确保CSRF防御措施有效
- 随着代码库的不断更新,原本有效的防御措施可能会被无意中破坏。
- 进行渗透测试,模拟真实攻击场景,验证防御措施的有效性。
- 使用自动化测试工具(如OWASP ZAP)定期扫描应用程序,发现潜在的CSRF漏洞。
-
保持Web应用程序和依赖库的更新,以防止已知漏洞被利用
- 许多Web框架和库都曾被发现存在CSRF漏洞,及时更新可以修复这些已知问题。
- 使用依赖管理工具(如npm、pip)定期检查并更新存在安全隐患的依赖包。
- 关注安全公告和漏洞数据库(如CVE),及时了解并应对新出现的CSRF相关漏洞。