CSRF

CSRF 的攻击原理、成立条件与常见防御方案说明

#tech / dev / frontend #status / growing #type / concept #platform / browser

[!info] related notes

CSRF

Overview

  1. 用户输入账号信息请求登录A网站。
  2. A网站验证用户信息,通过验证后返回给用户一个cookie
  3. 在未退出网站A之前,在同一浏览器中请求了黑客构造的恶意网站B
  4. B网站收到用户请求后返回攻击性代码,构造访问A网站的语句
  5. 浏览器收到攻击性代码后,在用户不知情的情况下携带cookie信息请求了A网站。此时A网站不知道这是由B发起的。那么这时黑客就可以进行一下骚操作了!

第3,4步细节

hacker 网站构造代码样例

<img src="https://bank.com/transfer?toAccount=hacker&amount=10000" width="0" height="0" />

浏览器原生支持跨站资源访问,当访问 hacker 网站时,浏览器看到资源地址,就会自动向该服务器发起请求。并且浏览器会自动看是否存在对应网址的 cookie,有的话自动发送SameSite 决定跨站场景下这个自动携带是否允许。

网络安全相关

挖掘

  1. 抓取一个正常请求的数据包,如果没有Referer字段和token,那么极有可能存在CSRF漏洞
  2. 如果有Referer字段,但是去掉Referer字段后再重新提交,如果该提交还有效,那么基本上可以确定存在CSRF漏洞。
  3. 利用工具进行CSRF检测。如:CSRFTESTER,CSRF REQUEST BUILDER等

CSRF 攻击成功的三个必要条件

黑客要成功实施 CSRF 攻击,必须同时满足以下条件:

  • 目标网站的接口存在漏洞: 接口完全依赖 Cookie 进行身份验证,且没有其他防伪造措施。
  • 用户处于登录状态: 用户刚刚登录过目标网站,且 Cookie 尚未过期。
  • 能猜出请求的所有参数: 如果转账接口除了需要 toAccountamount,还需要一个用户每次都必须手动输入的支付密码,那 CSRF 就会失败,因为黑客所在的 evil.com 无法读取 bank.com 的数据(受同源策略保护),自然猜不到密码。

防御

  • 验证 HTTP Referer 字段:在 HTTP 头中有一个字段叫 Referer,它记录了该 HTTP 请求的来源地址
  • 在请求地址中添加 token 并验证
  • 在 HTTP 头中自定义属性并验证

这是目前防御 CSRF 最简单、最有效的方法。后端在 Set-Cookie 时,加上 SameSite 属性。SameSite

  • SameSite=Strict 最严格。只有当前网页的 URL 与请求目标一致时,才会带上 Cookie。也就是说,如果你在 evil.com 发起对 bank.com 的请求,Cookie 绝对不会被带上。CSRF 直接失效。
  • SameSite=Lax(现代浏览器默认值): 稍微放宽一点。允许部分跨站的 GET 请求(比如你通过 <a> 标签从外部网站跳转到 bank.com 时会带 Cookie,保证用户体验),但跨站的 POST、PUT、DELETE 等修改数据的请求,或者通过 AJAX、图片加载发出的 GET 请求,统统不带 Cookie。
  • 注意:虽然现代浏览器默认 Lax,但老旧浏览器可能不支持,所以不能完全依赖它。

防御方案 B:CSRF Token(同步令牌模式,最经典的防御)

如果你的应用架构必须使用 Cookie,且需要兼容老浏览器,这是最标准的做法。

  1. 下发 Token: 用户登录后,服务器生成一个随机的、不可预测的字符串(CSRF Token)。这个 Token 可以通过专门的接口发给前端,或者嵌在 HTML 页面里(如果是 SSR 服务端渲染)。
  2. 请求携带: 前端在发送修改数据的请求(POST/PUT等)时,除了浏览器自动带上的 Cookie 外,还需要手动将这个 CSRF Token 放在请求的 Header(如 X-CSRF-Token)或者请求体(Body)中
  3. 服务器验证: 服务器收到请求,既验证 Cookie 里的 Session,又验证 Header 里的 CSRF Token 是否正确。
  4. 为什么这能防住? 因为黑客所在的 evil.com 受到浏览器的同源策略 (Same-Origin Policy) 限制,无法通过代码读取到 bank.com 返回的 CSRF Token。黑客只能让浏览器自动带 Cookie,却无法伪造出那个放在 Header 里的随机 Token,请求就会被后端拒绝。

这就是为什么我们在上一个问题里说,“JWT 存在 localStorage 中天生免疫 CSRF”。 如果你不使用 Cookie 来维持会话,而是要求前端每次请求都把 Token 放在 Authorization: Bearer <token> 这样的自定义 Header 中,CSRF 攻击就无从下手了。因为浏览器从来不会“自动”帮你加上自定义 Header。跨站伪造的请求根本不会携带用户的凭证。

创建于 2026/3/12 更新于 2026/5/27