跳到主要内容

CSRF(跨站请求伪造)

威胁描述

CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种攻击方式,攻击者通过欺骗用户在已认证的Web应用程序上执行非预期的操作。由于浏览器会自动携带用户的认证凭证(如Cookie),Web应用程序无法区分请求是来自用户主动操作还是来自攻击者的诱导。

工作原理

  1. 用户登录信任的网站A,并获得认证Cookie
  2. 攻击者诱导用户访问恶意网站B
  3. 恶意网站B向网站A发送一个请求,执行攻击者预设的操作(如转账、修改密码等)
  4. 浏览器自动携带用户在网站A的认证Cookie
  5. 网站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

最佳实践

  1. 对所有状态改变的请求实施CSRF保护,而不仅仅是敏感操作

    • 即使是看似不敏感的操作(如更改用户头像、更新个人资料)也可能被攻击者利用。例如,攻击者可能通过更改用户的联系方式来绕过后续的安全验证。
    • 一致的保护策略可以避免因选择性实施而产生的安全漏洞。
  2. 优先使用CSRF令牌结合SameSite Cookie属性的双重防护

    • CSRF令牌提供主动验证机制,确保请求来自合法用户的主动操作。
    • SameSite Cookie提供被动防御,限制Cookie仅在同一站点请求中发送,有效阻止跨站请求携带认证凭证。
    • 双重防护可以应对各种复杂的攻击场景,即使一种防御措施失效,另一种仍能提供保护。
  3. 避免使用GET请求执行状态改变操作

    • GET请求容易被攻击者通过图片标签、链接、iframe等方式诱导用户发送。
    • GET请求会被浏览器缓存,增加了攻击的持久性风险。
    • 遵循HTTP规范,GET请求应仅用于获取数据,而POST、PUT、DELETE等请求应用于修改数据。
  4. 对于API接口,使用令牌认证(如JWT)而非仅依赖Cookie

    • API接口通常被各种客户端(如移动应用、第三方服务)调用,令牌认证更加灵活。
    • 将令牌放在请求头中而非Cookie中,可以避免基于Cookie的CSRF攻击。
    • JWT等令牌可以包含更丰富的权限信息,实现更细粒度的访问控制。
  5. 定期进行安全测试,确保CSRF防御措施有效

    • 随着代码库的不断更新,原本有效的防御措施可能会被无意中破坏。
    • 进行渗透测试,模拟真实攻击场景,验证防御措施的有效性。
    • 使用自动化测试工具(如OWASP ZAP)定期扫描应用程序,发现潜在的CSRF漏洞。
  6. 保持Web应用程序和依赖库的更新,以防止已知漏洞被利用

    • 许多Web框架和库都曾被发现存在CSRF漏洞,及时更新可以修复这些已知问题。
    • 使用依赖管理工具(如npm、pip)定期检查并更新存在安全隐患的依赖包。
    • 关注安全公告和漏洞数据库(如CVE),及时了解并应对新出现的CSRF相关漏洞。