# 跨站请求伪造 (CSRF)

# 1. 介绍

CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。

攻击者盗用了你的身份,以你的名义发送恶意请求。对服务器来说这个请求是完全合法的,但却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。

# CSRF攻击原理及过程

  1. 用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
  2. 在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
  3. 用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
  4. 网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
  5. 浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。

CSRF攻击过程

# Cookie的主要特性

  1. 浏览器默认自动携带本次HTTP请求域名的Cookie(不管是通过什么方式,在什么页面发送的HTTP请求)
  2. 读写Cookie有跨域限制(作用域,Domain,Path)
  3. 生命周期(会话or持久)

# 2. 攻击对象

  • 个人用户:盗取用户资金(包括虚拟货币)、操控用户账户等。
  • 企业用户:盗取公司资金、查询内部数据、删除数据、更改公司网站等。

# 3. 特点

  • 依靠用户登录网站后获取的Cookie进行攻击,当用户没有退出网站时,攻击就会一直存在
  • 攻击者不能直接获取到Cookie等信息,只是盗用
  • 跨站请求伪造攻击具有隐蔽性,用户甚至在毫不知情的情况下帮助攻击者执行了攻击操作

# 4. 防范措施

上文中讲了CSRF的两个特点:

  • CSRF(通常)发生在第三方域名

  • CSRF攻击者不能获取到Cookie等信息,只是使用

针对这两点,我们可以专门制定防护策略:

  • 阻止不明外域的访问

  • 提交时要求附加本域才能获取的信息

# 4.1 验证 HTTP Referer 字段

根据 HTTP 协议,在 HTTP 头中有一个字段叫 Referer,它记录了该 HTTP 请求的来源地址。也就是说,服务器会验证客户端的请求来源,如果本网站请求的则响应,否则不响应。

处理了下面几种特殊情况,用Referrer防CSRF是安全

  • 读操作不能有上面提到提到的两种特殊情况,不能用JSONP,CORS要白名单,所以读操作是安全的。
  • 写操作Referrer为空的时候不能放过,使用白名单机制,Referrer在白名单内才放过。什么时候会为空?
    • 地址栏直接输入url的时候,第一个请求Referrer为空,一般是html页面,这种读操作不用防CSRF
    • 使用Referrer-Policy策略设置no-referrer是,Referrer为空,自己的页面不要这样设置,为了防黑客的页面设置了,所以为空的时候不能放过。
    • 还有一些iframe的特殊使用(以前用来绕过图片防盗链的)也会导致Referrer为空,这些情况都不能放过过。
  • 写操作不能用Get,如果写操作可以用Get,由于Img标签,A标签能发Get请求。所以在一些UGC网站,比如用户写日志可以插入自定义图片,能插入自定义连接,图片img标签src或者A标签的href就指向写操作的URL,这样只要打开这篇日志就会发送这个Get请求,或者点击了日志上的连接,就帮用户做了写操作。并且Referrer还是合法的。这就是一种少见的CSRF攻击过程,其实也是最早期的CSRF攻击。这种攻击一旦成功,很方便做成蠕虫病毒,危害性极大。PS有人觉得这种少见的攻击过程不算CSRF,应该算XSS,好像也有点道理,但是常规的防XSS的方法貌似不好防这种特殊情况,下面要讲的CSRF的Token的防护方式是能防这种特殊情况的。

同理适用Origin:

原理跟Referrer一样,Origin请求头是XHR2.0里增加的,含义是发送请求页面的域名,主要目的是解决跨域问题。如果用来校验CSRF请求,就有一些细节要处理好,后台判断Origin时也要使用白名单,并且不能为空,不在白名单内的请求都直接返回失败,不能执行请求里的写操作(有一些web server是请求执行了,也返回了数据,只是没有配ACAO响应头,浏览器收不到,这种情况能限制跨域请求,但是不能防CSRF的写操作)。另外一种做法就是自定义HTTP请求头,把HTTP请求升级为复杂请求,这样在跨域情况下就会先发一个Option预检请求,预检请求通不过也就不会执行后面真实请求了。

# 4.2 使用 token (Anti CSRF Token)

现在业界对CSRF的防御,一致的做法是使用一个Token(Node、PHP、Java等服务端框架对CSRF的防御都已经有内建的方案)。

CSRF攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于Cookie中,因此黑客可以在不知道这些验证信息的情况下直接利用用户自己的Cookie来通过安全验证。

要抵御CSRF,关键在于在请求中放入黑客所不能伪造的信息,并且该信息不存在于Cookie之中。可以在用户访问网站页面时,向页面注入一个Token,然后再用户提交数据时,将Token一同提交给服务器,服务器进行对比,如果Token不一致,则拒绝执行。

具体流程如下:

  1. 用户访问某个表单页面。
  2. 服务端生成一个Token,放在用户的Session中,或者浏览器的Cookie里。
  3. 在页面表单附带上Token参数。
  4. 用户提交请求后, 服务端验证表单中的Token是否与用户Session(Cookie)中的Token一致,一致为合法请求,不是则拒绝请求。

Token就是一个令牌,当用户提交请求时,这个Token会告诉服务器:“嘿,是我自己本人发起的请求,不是黑客伪造的哦!”

攻击者可以伪造表单,但是他没有办法伪造Token,因为Token是放在Session或者Cookie里的,攻击者无法获取。

# 4.3 验证码

验证码被认为是对抗CSRF攻击最简洁而有效的防御方法。

CSRF攻击的过程,往往是在用户不知情的情况下构造了网络请求。而验证码会强制用户必须与应用进行交互,才能完成最终请求。因为通常情况下,验证码能够很好地遏制CSRF攻击。

但是出于用户体验考虑,网站不能给所有的操作都加上验证码。因此,验证码只能作为一种辅助手段,不能作为主要解决方案。

# 4.4 SameSite限制

Chrome 51 开始,浏览器的 Cookie 新增加了一个SameSite属性,用来防止 CSRF 攻击和用户追踪。

Cookie 的SameSite属性用来限制第三方 Cookie,从而减少安全风险,但是客户端对SameSite属性的支持并不是特别好

它可以设置三个值。

  • Strict
  • Lax
  • None

# 4.4.1 Strict

Strict最为严格,完全禁止第三方 Cookie,跨站点时,任何情况下都不会发送 Cookie。换言之,只有当前网页的 URL 与请求目标一致,才会带上 Cookie。

# 4.4.2 Lax

Lax规则稍稍放宽,大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外。

导航到目标网址的 GET 请求,只包括三种情况:链接,预加载请求,GET 表单。

<a href="..."></a>
<link rel="prerender" href="..."/>
<form method="GET" action="...">
1
2
3

设置了Strict或Lax以后,基本就杜绝了 CSRF 攻击。当然,前提是用户浏览器支持 SameSite 属性

# 4.4.3 None

Chrome 计划将Lax变为默认设置。这时,网站可以选择显式关闭SameSite属性,将其设为None。不过,前提是必须同时设置Secure属性(Cookie 只能通过 HTTPS 协议发送),否则无效

# 5. 参考资料

web安全之csrf攻击 (opens new window)

CSRF攻击防御原理 (opens new window)

前端安全系列之二:如何防止CSRF攻击? (opens new window)

SameSite cookies (opens new window)

Last Updated: 9/2/2025, 3:13:27 PM