引言:悄无声息的“提线木偶”攻击

你已经登录了网上银行,此时不小心点击了一个恶意链接或访问了一个恶意网站,这个网站暗中向银行服务器发起了一个转账请求,而你的资金就在你毫不知情的情况下被转走了。这就是跨站请求伪造攻击。与XSS利用用户对网站的信任不同,CSRF利用的是网站对用户浏览器的信任。攻击者欺骗用户的浏览器,让其以用户的身份向一个受信任的网站发送一个非用户本意的请求。

一、 CSRF的攻击原理与条件

一次成功的CSRF攻击需要两个关键条件:

  1. 用户已登录目标网站(A),并且会话Cookie尚未过期。​ 浏览器会自动在请求中携带该Cookie。
  2. 用户访问了恶意网站(B),该网站会自动或诱使用户向网站A发出一个请求。
  3. 攻击流程:
  1. 用户登录bank.com,服务器返回包含会话Cookie的响应。
  2. 用户在同一浏览器中访问了evil.com。
  3. evil.com的页面中包含一个隐藏的表单或图片,其src指向bank.com/transfer?to=attacker&amount=10000。
  4. 浏览器在向bank.com发起请求时,会自动携带步骤1中的会话Cookie。
  5. bank.com服务器验证Cookie有效,认为是用户发起的合法请求,于是执行转账操作。

二、 防御CSRF:打破攻击链

防御的核心思路是:让攻击者无法伪造出能被服务器识别的合法请求。

1. 同源检测:利用HTTP头部

  • Origin Header:​ 对于POST请求,浏览器会自动添加Origin头,表明请求的来源。服务器可以验证此头是否来自受信任的源(本站)。
  • Referer Header:​ 包含了请求页面的完整URL。同样可以用于验证来源。但有时用户出于隐私会禁用Referer,且其可能被篡改,可靠性稍逊于Origin。
  • 2. CSRF Tokens(最主流、最有效的方法)
  • 原理:在请求中加入一个攻击者无法预测、无法获取的随机数。
  • 生成:​ 服务器在用户会话(Session)中生成一个随机的、复杂的Token。
  • 携带:​ 在渲染表单时,将此Token作为一个隐藏字段(<input type="hidden" name="csrf_token" value="random123">)嵌入表单。对于AJAX请求,可以将其放在请求头中(如X-CSRF-TOKEN)。
  • 验证:​ 当用户提交表单时,服务器检查请求中的Token是否与会话中存储的Token一致。如果不一致,则拒绝请求。
  • 由于恶意网站evil.com受同源策略限制,无法读取bank.com页面中的Token值,因此它发起的请求中必然不包含或包含错误的Token,从而被服务器拒绝。
  • 3. 双重Cookie验证
  • 将Cookie作为Token使用的一种简化方案。
  • 前端JavaScript从Cookie中读取一个特定的值(例如csrf_cookie)。
  • 在发起请求时,将此值作为参数或请求头(如X-CSRF-TOKEN)一并提交。
  • 服务器验证请求中的这个值是否与Cookie中的值一致。
  • 此方法不如CSRF Token安全,因为如果网站存在XSS漏洞,Cookie可能被窃取,从而导致此防御失效。
  • 4. SameSite Cookie属性(现代浏览器的福音)
  • 通过设置Cookie的SameSite属性,可以控制浏览器在跨站请求时是否发送Cookie。
  • SameSite=Strict:最严格,完全禁止在跨站请求中发送Cookie。
  • SameSite=Lax:宽松模式,允许部分安全的跨站请求(如GET请求的导航)携带Cookie。
  • SameSite=None:允许跨站发送,但必须同时设置Secure属性(HTTPS)。
  • 设置SameSite=Lax或Strict可以有效地防御大多数CSRF攻击。

三、 防御策略总结与最佳实践

  • 首选方案:CSRF Token + SameSite Cookie。​ 这是目前最坚固的防御组合。
  • 关键操作禁用GET:​ 像转账、修改密码等操作,必须使用POST(或PUT, DELETE)方法,避免通过GET请求的URL就能触发敏感操作。
  • 二次认证:​ 对于极其敏感的操作(如大额转账),要求用户再次输入密码或进行短信/邮件验证。

四、 总结

CSRF是一种利用信任关系的“偷袭”战术。防御的关键在于让服务器有能力区分“来自用户的真实请求”和“攻击者伪造的请求”。CSRF Token机制是实现这一目标的核心手段。同时,现代浏览器提供的SameSite Cookie属性为我们提供了强大且易于部署的辅助防御。结合敏感操作使用POST请求、必要时进行二次认证,可以构建一个让攻击者无从下手的坚固防线。