简介

CSRF(Cross-site request forgery)跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。

一个典型的CSRF攻击有着如下的流程:

  • 受害者登录a.com,并保留了登录凭证(Cookie)。
  • 攻击者引诱受害者访问了thirdsite.com。
  • thirdsite.coma.com 发送了一个请求:a.com/act=xx
  • a.com接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求。
    • thirdsite.com此时拿到了 a.com给用户种下的cookie,所以thirdsite.com 请求的时候也带上了a.com网站的cookie,从而绕过了验证
  • a.com以受害者的名义执行了act=xx。
  • 攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让a.com执行了自己定义的操作。


thirdsite.com在发出请求的时候 可以 携带 a.com的网站的cookie?
===> thirdsite.com 发出的请求是 a.com/action=xx,所以浏览器侧看到请求是a.com就会自动带上a网站的cookie;
很明显 thirdsite.com域名下 发出 a.com 的请求,是发生了跨域行为的,一般浏览器会对跨域请求的返回进行拦截;但是发还是发的出去的啊!
所以一般用于,对用户数据进行服务器访问的更改请求,而拿不到服务器返回的数据

在这个攻击过程中,攻击者借助受害者的 Cookie 骗取服务器的信任,但并不能拿到 Cookie,也看不到 Cookie 的内容。
而对于服务器返回的结果,由于浏览器同源策略的限制,攻击者也无法进行解析
(攻击者的网站虽然是跨域的,但是他构造的链接(/请求)是源网站的,跟源网站是同源的,所以能够携带cookie发起访问)。

但是攻击者无法从返回的结果中得到任何东西,他所能做的就是给服务器发送请求,以执行请求中所描述的命令,在服务器端直接改变数据的值,而非窃取服务器中的数据。例如删除数据、修改数据,新增数据等,无法获取数据。

常见CSRF攻击

GET类型

GET类型的CSRF利用非常简单,只需要一个HTTP请求,一般会这样利用:

  1. <img src="http://bank.example/withdraw?amount=10000&for=hacker" >

在受害者访问含有这个img的页面后,浏览器会自动向http://bank.example/withdraw?account=xiaoming&amount=10000&for=hacker发出一次HTTP请求。bank.example就会收到包含受害者登录信息的**一次跨域请求**。

POST类型

这种类型的CSRF利用起来通常使用的是一个自动提交的表单,如:

  1. <form action="http://bank.example/withdraw" method=POST>
  2. <input type="hidden" name="account" value="xiaoming" />
  3. <input type="hidden" name="amount" value="10000" />
  4. <input type="hidden" name="for" value="hacker" />
  5. </form>
  6. <script> document.forms[0].submit(); </script>

链接类型

诱骗点击,点击的一瞬间发出get请求

  1. <a href="http://test.com/csrf/withdraw.php?amount=1000&for=hacker" taget="_blank">
  2. 重磅消息!!
  3. <a/>


防范

CSRF的两个特点:

  • CSRF(通常)发生在第三方域名。
  • CSRF攻击者不能获取到Cookie等信息,只是使用。

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

  • 阻止不明外域的访问
    • 同源检测
    • Samesite Cookie
  • 提交时要求附加本域才能获取的信息
    • CSRF Token
    • 双重Cookie验证

同源策略检测

既然CSRF大多来自第三方网站,那么我们就直接禁止外域(或者不受信任的域名)对我们发起请求

如何判断请求是否来自外域呢?

在HTTP协议中,每一个异步请求都会携带两个Header,用于标记来源域名:

  • Origin Header
  • Referer Header

但是Origin在以下两种情况下并不存在: IE 11 不会在跨站CORS请求上添加Origin标头;302重定向;
所以一般用referer

使用验证 Referer 值的方法,就是把安全性都依赖于第三方(即浏览器)来保障,从理论上来讲,这样并不是很安全。在部分情况下,攻击者可以隐藏,甚至修改自己请求的Referer。

Token

CSRF的另一个特征是,攻击者无法直接窃取到用户的信息(Cookie,Header,网站内容等),仅仅是冒用Cookie中的信息

而CSRF攻击之所以能够成功,是因为服务器误把攻击者发送的请求当成了用户自己的请求。那么我们可以要求所有的用户请求都携带一个CSRF攻击者无法获取到的Token
(当然是前端工程师手动写的啦 而不是依靠浏览器自动带上;手动将给请求的header注入token)

服务器通过校验请求是否携带正确的Token,来把正常的请求和攻击的请求区分开,也可以防范CSRF的攻击

cookie改良

samesite属性

Set-Cookie响应头新增Samesite属性,它用来标明这个 Cookie是个“同站 Cookie”,同站Cookie只能作为第一方Cookie,不能作为第三方Cookie
image.png
Samesite=Strict
表明这个 Cookie 在任何情况下都不可能作为第三方 Cookie,绝无例外。比如说 b.com 设置了如下 Cookie:
lax:假如这个请求是这种请求(改变了当前页面或者打开了新页面)且同时是个GET请求,则这个Cookie可以作为第三方Cookie;异步请求就不行 or post的表单提交也不行;

  1. # 在 a.com 下发起对 b.com 的任意请求,
  2. # foo 这个 Cookie 都不会被包含在 Cookie 请求头中
  3. Set-Cookie: foo=1; Samesite=Strict
  4. # bar这个cookie会包含在请求中
  5. Set-Cookie: bar=2; Samesite=Lax
  6. Set-Cookie: baz=3

验证码

验证码被认为是对抗 CSRF 攻击最简洁而有效的防御方法。从上述示例中可以看出,CSRF 攻击往往是在用户不知情的情况下构造了网络请求。
验证码会强制用户必须与应用进行交互才能完成最终请求。因为通常情况下,验证码能够很好地遏制 CSRF 攻击。但验证码并不是万能的,因为出于用户考虑,不能给网站所有的操作都加上验证码。因此,验证码只能作为防御 CSRF 的一种辅助手段,而不能作为最主要的解决方案。

参考资料

美团-前端安全系列之二:如何防止CSRF攻击?