Web 安全

CSRF (跨站请求伪造)

约 3 分钟阅读

什么是 CSRF (跨站请求伪造)

CSRF (Cross-Site Request Forgery) 是一种攻击手法,使用户的浏览器向已认证的 Web 站点发送非用户本意的请求。攻击者准备一个陷阱网页或邮件,受害者一旦查看,其浏览器就会自动向目标站点发送恶意请求。

由于浏览器会自动为同一域名的请求附加 Cookie,如果受害者处于登录状态,攻击者的请求也会被作为已认证请求处理。密码修改、转账、邮箱地址变更、商品购买等改变状态的操作都会成为攻击目标。

攻击原理与具体示例

CSRF 攻击按以下流程执行。

  1. 受害者登录目标站点 (例如网上银行),会话 Cookie 保存在浏览器中
  2. 受害者查看攻击者准备的陷阱页面 (通过邮件链接、论坛帖子等)
  3. 陷阱页面中嵌入的 HTML 或 JavaScript 从受害者的浏览器向目标站点发送请求
  4. 浏览器自动附加会话 Cookie,目标站点将其作为合法请求处理

例如,如果转账功能通过 POST /transfer?to=attacker&amount=100000 这样的请求运行,攻击者只需在陷阱页面中嵌入自动提交的表单,就能从受害者账户执行转账。

虽然容易与 XSS 混淆,但 CSRF 的根本区别在于它是从受害者的浏览器伪造合法请求,而非在受害者的浏览器中执行攻击者的脚本。

防御措施的实现

CSRF 防御的核心是引入验证请求是否基于合法用户意图的机制。

CSRF 令牌

服务器在显示表单时生成随机令牌并嵌入隐藏字段。表单提交时验证令牌是否匹配,从而拒绝来自外部站点的伪造请求。令牌对每个会话是唯一的,攻击者无法获知受害者的令牌。

SameSite Cookie 属性

通过设置 CookieSameSite 属性,可以控制跨站请求是否附加 Cookie。

  • SameSite=Strict:不为来自外部站点的任何请求附加 Cookie。最安全,但通过外部链接访问时也无法保持登录状态
  • SameSite=Lax:仅为顶级导航 (链接点击) 的 GET 请求附加 Cookie。由于不为 POST 请求附加 Cookie,可以防止大多数 CSRF 攻击
  • SameSite=None:跨站请求也附加 Cookie。必须与 Secure 属性配合使用

其他防御措施

  • Origin / Referer 头验证:确认请求来源是否为本站。但由于隐私设置或代理,Referer 可能不会被发送
  • 要求自定义头:要求 X-Requested-With 等自定义头。由于浏览器的同源策略,跨站请求添加自定义头需要 CORS 预检,从而可以防止 CSRF
  • 正确配置安全头:增强整体安全态势

现代框架中的 CSRF 防护

许多现代 Web 框架内置了 CSRF 防护功能。

  • Django:通过 {% csrf_token %} 模板标签自动嵌入令牌,并通过中间件进行验证
  • Ruby on Rails:通过 protect_from_forgery 自动生成和验证 CSRF 令牌
  • Spring Security:默认启用 CSRF 保护。自动将 CsrfToken 插入表单
  • Next.js / SPA:API 路由中结合 SameSite Cookie 和 Origin 头验证。也可考虑通过 WAF 进行额外保护

如果禁用框架的 CSRF 保护,请确认替代措施已可靠实现。在 API 端点排除 CSRF 保护时,前提是切换到 Bearer Token 认证,不使用基于 Cookie 的认证。

常见误解

GET 请求不会发生 CSRF
如果通过 GET 请求实现了状态变更操作 (删除、设置变更等),就会成为 CSRF 的目标。仅通过在 img 标签的 src 属性中设置 URL 就能发送 GET 请求,因此状态变更必须使用 POST/PUT/DELETE 实现。
使用 HTTPS 就能防止 CSRF
HTTPS 提供通信加密和防篡改功能,但与 CSRF 无关。CSRF 是从合法用户的浏览器发送合法请求的攻击,即使通信被加密,攻击仍然成立。
分享

相关术语