csrf攻击

Posted Web News

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了csrf攻击相关的知识,希望对你有一定的参考价值。

什么是CSRF

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


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

  • 受害者登录a.com,并保留了登录凭证(Cookie)。

  • 攻击者引诱受害者访问了b.com。

  • b.com 向 a.com 发送了一个请求:a.com/act=xx。浏览器会…

  • a.com接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求。

  • a.com以受害者的名义执行了act=xx。

  • 攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让a.com执行了自己定义的操作。

几种常见的攻击类型

  • GET类型的CSRF


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

 <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

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

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

访问该页面后,表单会自动提交,相当于模拟用户完成了一次POST操作。

POST类型的攻击通常比GET要求更加严格一点,但仍并不复杂。任何个人网站、博客,被黑客上传页面的网站都有可能是发起攻击的来源,后端接口不能将安全寄托在仅允许POST上面。

  • 链接类型的CSRF

链接类型的CSRF并不常见,比起其他两种用户打开页面就中招的情况,这种需要用户点击链接才会触发。这种类型通常是在论坛中发布的图片中嵌入恶意链接,或者以广告的形式诱导用户中招,攻击者通常会以比较夸张的词语诱骗用户点击,例如:

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

由于之前用户登录了信任的网站A,并且保存登录状态,只要用户主动访问上面的这个PHP页面,则表示攻击成功

CSRF的特点

  • 攻击一般发起在第三方网站,而不是被攻击的网站。被攻击的网站无法防止攻击发生。

  • 攻击利用受害者在被攻击网站的登录凭证,冒充受害者提交操作;而不是直接窃取数据。

  • 整个过程攻击者并不能获取到受害者的登录凭证,仅仅是“冒用”。

  • 跨站请求可以用各种方式:图片URL、超链接、CORS、Form提交等等。部分请求方式可以直接嵌入在第三方论坛、文章中,难以进行追踪。


CSRF通常是跨域的,因为外域通常更容易被攻击者掌控。但是如果本域下有容易被利用的功能,比如可以发图和链接的论坛和评论区,攻击可以直接在本域下进行,而且这种攻击更加危险。

防护策略

CSRF通常从第三方网站发起,被攻击的网站无法防止攻击发生,只能通过增强自己网站针对CSRF的防护能力来提升安全性。

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

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

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

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

  • 阻止不明外域的访问

    • 同源检测

    • Samesite Cookie

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

    • CSRF Token

    • 双重Cookie验证

以下我们对各种防护方法做详细说明:

同源检测

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

那么问题来了,我们如何判断请求是否来自外域呢?

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

  • Origin Header

  • Referer Header

这两个Header在浏览器发起请求时,大多数情况会自动带上,并且不能由前端自定义内容。服务器可以通过解析这两个Header中的域名,确定请求的来源域。

使用Origin Header确定来源域名

在部分与CSRF有关的请求中,请求的Header中会携带Origin字段。字段内包含请求的域名(不包含path及query)。

如果Origin存在,那么直接使用Origin中的字段确认来源域名就可以。

但是Origin在以下两种情况下并不存在:

  • IE11同源策略: IE 11 不会在跨站CORS请求上添加Origin标头,Referer头将仍然是唯一的标识。最根本原因是因为IE 11对同源的定义和其他浏览器有不同,有两个主要的区别,可以参考MDN Same-origin_policy#IE_Exceptions

  • 302重定向: 在302重定向之后Origin不包含在重定向的请求中,因为Origin可能会被认为是其他来源的敏感信息。对于302重定向的情况来说都是定向到新的服务器上的URL,因此浏览器不想将Origin泄漏到新的服务器上。

使用Referer Header确定来源域名

这种方法并非万无一失,Referer的值是由浏览器提供的,虽然HTTP协议上有明确的要求,但是每个浏览器对于Referer的具体实现可能有差别,并不能保证浏览器自身没有安全漏洞。使用验证 Referer 值的方法,就是把安全性都依赖于第三方(即浏览器)来保障,从理论上来讲,这样并不是很安全。在部分情况下,攻击者可以隐藏,甚至修改自己请求的Referer。

2014年,W3C的Web应用安全工作组发布了Referrer Policy草案,对浏览器该如何发送Referer做了详细的规定。截止现在新版浏览器大部分已经支持了这份草案,我们终于可以灵活地控制自己网站的Referer策略了。新版的Referrer Policy规定了五种Referer策略:No Referrer、No Referrer When Downgrade、Origin Only、Origin When Cross-origin、和 Unsafe URL。之前就存在的三种策略:never、default和always,在新标准里换了个名称。他们的对应关系如下:

根据上面的表格因此需要把Referrer Policy的策略设置成same-origin,对于同源的链接和引用,会发送Referer,referer值为Host不带Path;跨域访问则不携带Referer。例如:aaa.com引用bbb.com的资源,不会发送Referer。

设置Referrer Policy的方法有三种:

  1. 在CSP设置

  2. 页面头部增加meta标签

  3. a标签增加referrerpolicy属性

上面说的这些比较多,但我们可以知道一个问题:攻击者可以在自己的请求中隐藏Referer。如果攻击者将自己的请求这样填写:

 <img src="http://bank.example/withdraw?amount=10000&for=hacker" referrerpolicy="no-referrer"> 

那么这个请求发起的攻击将不携带Referer。

另外在以下情况下Referer没有或者不可信:

1.IE6、7下使用window.location.href=url进行界面的跳转,会丢失Referer。

2.IE6、7下使用window.open,也会缺失Referer。

3.HTTPS页面跳转到HTTP页面,所有浏览器Referer都丢失。

4.点击Flash上到达另外一个网站的时候,Referer的情况就比较杂乱,不太可信。

无法确认来源域名情况

当Origin和Referer头文件不存在时该怎么办?如果Origin和Referer都不存在,建议直接进行阻止,特别是如果您没有使用随机CSRF Token(参考下方)作为第二次检查。


如何阻止外域请求

通过Header的验证,我们可以知道发起请求的来源域名,这些来源域名可能是网站本域,或者子域名,或者有授权的第三方域名,又或者来自不可信的未知域名。

我们已经知道了请求域名是否是来自不可信的域名,我们直接阻止掉这些的请求,就能防御CSRF攻击了吗?

且慢!当一个请求是页面请求(比如网站的主页),而来源是搜索引擎的链接(例如百度的搜索结果),也会被当成疑似CSRF攻击。所以在判断的时候需要过滤掉页面请求情况,通常Header符合以下情况:

Accept: text/htmlMethod: GET

但相应的,页面请求就暴露在了CSRF的攻击范围之中。如果你的网站中,在页面的GET请求中对当前用户做了什么操作的话,防范就失效了。

例如,下面的页面请求:

GET https://example.com/addComment?comment=XXX&dest=orderId

注:这种严格来说并不一定存在CSRF攻击的风险,但仍然有很多网站经常把主文档GET请求挂上参数来实现产品功能,但是这样做对于自身来说是存在安全风险的。

另外,前面说过,CSRF大多数情况下来自第三方域名,但并不能排除本域发起。如果攻击者有权限在本域发布评论(含链接、图片等,统称UGC),那么它可以直接在本域发起攻击,这种情况下同源策略无法达到防护的作用。

综上所述:同源验证是一个相对简单的防范方法,能够防范绝大多数的CSRF攻击。但这并不是万无一失的,对于安全性要求较高,或者有较多用户输入内容的网站,我们就要对关键的接口做额外的防护措施。

CSRF Token

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

而CSRF攻击之所以能够成功,是因为服务器误把攻击者发送的请求当成了用户自己的请求。那么我们可以要求所有的用户请求都携带一个CSRF攻击者无法获取到的Token。服务器通过校验请求是否携带正确的Token,来把正常的请求和攻击的请求区分开,也可以防范CSRF的攻击。


原理

CSRF Token的防护策略分为三个步骤:

1.将CSRF Token输出到页面中

首先,用户打开页面的时候,服务器需要给这个用户生成一个Token,该Token通过加密算法对数据进行加密,一般Token都包括随机字符串和时间戳的组合,显然在提交时Token不能再放在Cookie中了,否则又会被攻击者冒用。因此,为了安全起见Token最好还是存在服务器的Session中,之后在每次页面加载时,使用JS遍历整个DOM树,对于DOM中所有的a和form标签后加入Token。这样可以解决大部分的请求,但是对于在页面加载之后动态生成的HTML代码,这种方法就没有作用,还需要程序员在编码时手动添加Token。


2.页面提交的请求携带这个Token

 <input type=”hidden” name=”csrftoken” value=”tokenvalue”/>

这样,就把Token以参数的形式加入请求了。


3.服务器验证Token是否正确

当用户从客户端得到了Token,再次提交给服务器的时候,服务器需要判断Token的有效性,验证过程是先解密Token,对比加密字符串以及时间戳,如果加密字符串一致且时间未过期,那么这个Token就是有效的。

这种方法要比之前检查Referer或者Origin要安全一些,Token可以在产生并放于Session之中,然后在每次请求时把Token从Session中拿出,与请求中的Token进行比对,但这种方法的比较麻烦的在于如何把Token以参数的形式加入请求。

双重Cookie验证

在会话中存储CSRF Token比较繁琐,而且不能在通用的拦截上统一处理所有的接口。

那么另一种防御措施是使用双重提交Cookie。利用CSRF攻击不能获取到用户Cookie的特点,我们可以要求Ajax和表单请求携带一个Cookie中的值。

双重Cookie采用以下流程:

  • 在用户访问网站页面时,向请求域名注入一个Cookie,内容为随机字符串(例如csrfcookie=v8g9e4ksfhw)。

  • 在前端向后端发起请求时,取出Cookie,并添加到URL的参数中(接上例POST https://www.a.com/comment?csrfcookie=v8g9e4ksfhw)。

  • 后端接口验证Cookie中的字段与URL参数中的字段是否一致,不一致则拒绝。

此方法相对于CSRF Token就简单了许多。可以直接通过前后端拦截的的方法自动化实现。后端校验也更加方便,只需进行请求中字段的对比,而不需要再进行查询和存储Token。

总结

用双重Cookie防御CSRF的优点:

  • 无需使用Session,适用面更广,易于实施。

  • Token储存于客户端中,不会给服务器带来压力。

  • 相对于Token,实施成本更低,可以在前后端统一拦截校验,而不需要一个个接口和页面添加。

缺点:

  • Cookie中增加了额外的字段。

  • 如果有其他漏洞(例如XSS),攻击者可以注入Cookie,那么该防御方式失效。

  • 难以做到子域名的隔离。

  • 为了确保Cookie传输安全,采用这种防御方式的最好确保用整站HTTPS的方式,如果还没切HTTPS的使用这种方式也会有风险。


Samesite Cookie属性


防止CSRF攻击的办法已经有上面的预防措施。为了从源头上解决这个问题,Google起草了一份草案来改进HTTP协议,那就是为Set-Cookie响应头新增Samesite属性,它用来标明这个 Cookie是个“同站 Cookie”,同站Cookie只能作为第一方Cookie,不能作为第三方Cookie,Samesite 有两个属性值,分别是 Strict 和 Lax,下面分别讲解:

Samesite=Strict
Set-Cookie: foo=1; Samesite=StrictSet-Cookie: bar=2; Samesite=LaxSet-Cookie: baz=3



我们在 a.com 下发起对 b.com 的任意请求,foo 这个 Cookie 都不会被包含在 Cookie 请求头中,但 bar 会。举个实际的例子就是,假如淘宝网站用来识别用户登录与否的 Cookie 被设置成了 Samesite=Strict,那么用户从百度搜索页面甚至天猫页面的链接点击进入淘宝后,淘宝都不会是登录状态,因为淘宝的服务器不会接受到那个 Cookie,其它网站发起的对淘宝的任意请求都不会带上那个 Cookie。


Samesite=Lax

Set-Cookie: foo=1; Samesite=StrictSet-Cookie: bar=2; Samesite=LaxSet-Cookie: baz=3

当用户从 a.com 点击链接进入 b.com 时,foo 这个 Cookie 不会被包含在 Cookie 请求头中,但 bar 和 baz 会,也就是说用户在不同网站之间通过链接跳转是不受影响了。但假如这个请求是从 a.com 发起的对 b.com 的异步请求,或者页面跳转是通过表单的 post 提交触发的,则bar也不会发送。


防止网站被利用


前面所说的,都是被攻击的网站如何做好防护。而非防止攻击的发生,CSRF的攻击可以来自:

  • 攻击者自己的网站。

  • 有文件上传漏洞的网站。

  • 第三方论坛等用户内容。

  • 被攻击网站自己的评论功能等。

对于来自黑客自己的网站,我们无法防护。但对其他情况,那么如何防止自己的网站被利用成为攻击的源头呢?

  • 严格管理所有的上传接口,防止任何预期之外的上传内容(例如HTML)。

  • 添加Header X-Content-Type-Options: nosniff 防止黑客上传HTML内容的资源(例如图片)被解析为网页。

  • 对于用户上传的图片,进行转存或者校验。不要直接使用用户填写的图片链接。

  • 当前用户打开其他用户填写的链接时,需告知风险(这也是很多论坛不允许直接在内容中发布外域链接的原因之一,不仅仅是为了用户留存,也有安全考虑)。

CSRF其他防范措施


原文:https://juejin.im/post/5bc009996fb9a05d0a055192



以上是关于csrf攻击的主要内容,如果未能解决你的问题,请参考以下文章

csrf攻防-攻击&防护代码演示

详解CSRF漏洞攻击

CSRF通过DVWA教你学会CSRF攻击

什么是CSRF攻击?什么是XSS攻击?解决方案有哪些?

python全栈系列之---xss跨站脚本攻击和csrf(xsrf)攻击

Xss和Csrf介绍