CSDF攻击详解

        做过Web开发的朋友或多或少都听说过CSRF攻击,但未必真的理解,因为我曾经也在好长一段时间里似懂非懂,比如以为CSRF攻击采用同源策略的限制就可以避免。希望看完本篇文章后能让您有更深的理解,最好有一种豁然开朗的感觉:)。
1、什么是CSRF攻击?
        CSRF,英文全称 Cross-site request forgery翻译过来就是“跨站请求伪造”,怎么理解呢?我们先来看一个例子:
        用户U首先通过浏览器成功登录了一个受信任的电商网站A(比如某宝某东),进入页面后做了些日常操作。这时候因为某些原因在没有关闭网站A页面的情况下又打开了一个新的浏览器页面(一般是通过tab方式打开),访问了一个可能不太安全的网站B(不安全为什么还要打开?别问我,问就是你懂的。),然后在网站的诱惑下做了一些操作,比如点了下按钮什么的。假如开发网站B的攻击者曾经对网站A非常了解(也许就是网站A曾经的高级码农),那么用户U可能就已经中招了。为什么会中招?因为在打开网站B或者说点击网站B页面的某个按钮的那一刻,很可能就执行了类似下面的代码:
<form action="https://网站A/methodC" method="post">
    <input type="hidden" name="amount" value="2000"/>
    <input type="hidden" name="account" value="tom"/>
    <input type="submit" value="点我就送!"/>
</form>

       这段代码的意思是通过表单调用网站A的某个方法向攻击者的账号转了2000元钱。而实际的攻击可能更隐蔽,比如使用JS自动完成上述代码的提交,你完全不知晓。等到你发现后想追回这笔钱?很遗憾,这个网站很可能是境外的,即使你知道被转账户是谁也无能为力。

2、产生CSRF攻击的原因?
现在我们从技术的角度去梳理下CSRF攻击是如何完成的。
  1. 受害用户登录了信任网站A,假如网站A在后端的鉴权是通过cookie-session机制完成的,那么此时session内容已被传到了客户端本地的cookie。
  2. 受害用户没有关闭网站A也没有注销登录,而是在同一浏览器里打开了网站B,然后触发了网站B的某个操作,恶意代码被执行,受害用户执行了一个他自己并不知道的操作,连同之前记录的session内容随cookie一同传给了网站A。
  3. 网站A收到这个请求,因为收到了cookie的session内容,同时跨站防护做的不严谨,以为是登录用户自己的请求操作,正常执行,悲剧就这样发生了。
见下图可能更好的理解(引用自 什么是CSRF攻击?如何防御CRSF攻击? - 知乎):

        因为用户访问的网站A和B是两个不同源的网站,而网站B利用了网站A的漏洞进行了攻击,所以这种攻击被称为CSRF即跨站请求伪造攻击。至于什么是同源网站后面会有解释。

我们来分析下产生CSRF攻击的必要条件:
  1. 用户执行了不合法的操作,这里特指在登录网站A后不注销的情况下在同一浏览器里打开了不安全网站B页面。
  2. 服务端采用了cookie-session机制鉴权。
  3. 服务端对跨站请求没有做严谨的防护,认为只要收到客户端发来的session内容就认为是登录用户的请求操作。
3、如何避免CSRF攻击?
        知道了被攻击的过程和原因,那怎么避免呢?
1从客户端用户来说很难避免,因为用户大多数不懂技术,要求他不打开网站B或者登录A后注销(清除本地cookie-session)再打开网站B是不现实的,再说,人家就是想访问网站B你也管不了啊。既然客户端无法避免,那我们只能从服务端来解决。继续往后看
2服务端采用cookie-session机制鉴权,可以改为非cookie传递参数的鉴权机制,比如Header携带JWT鉴权,从而避免跨站攻击。
        这里可能有些朋友会有疑问,浏览器传递cookie不是有同源策略的限制么?即不同源的网站之间cookie是不能互相访问的,那么网站B是怎么做到把网站A的cookie数据传到网站A的后端呢?我们来仔细解读下浏览器的同源策略:
所谓同源,就是指浏览器请求的网址有三个相同就是同源:
  • 协议相同,例如都是http或https
  • 域名相同,什么是域名相同请自行谷歌或百度
  • 端口号相同
如果非同源,有以下三种操作受到限制:
  • Cookie、 localStorage等保存在客户端机器上的数据无法读取;
  • DOM无法获得;例如恶意网站可以通过iframe打开某个操作界面,如果可以获取DOM就相当于可以获取整个操作界面的信息。
  • AJAX请求不能发送。
        但其中有个特例,即http实现规范里表单的提交不受同源策略的限制(说实话这个规定我不是特别理解),这也就解释了为什么上面的例子中网站B能将网站A的cooike数据发送给网站A,不相信的朋友可以自行写代码验证一下,我曾经就这在这里误解了很久。
另外,即使是ajax,可能由于网站A采用了前后端分离的方式部署,即前端域名和后端域名不同源,为了保证前后端数据交互,必须解除同源策略的限制。所以在某些网站同源策略的限制实际没有产生作用,ajax的请求默认允许跨站发送。(当然可以通过在服务端以白名单的形式解决这个问题,后面会深入讨论。)
        关于JWT鉴权的详解不是本文的重点,网上有大把文章阐述,请自行谷歌或百度,这里不再赘述。
3假如网站A在后端使用cooie-sessionid机制已经根深蒂固,很难重构,必须使用。那如何解决服务端对跨站请求做严谨防护呢?
通过前面的分析我们知道,产生漏洞的根本原因在于网站A出于某些原因无法从根本上执行同源策略的限制,无法阻止cookie跨站传递,或者说由于前后端分离的部署方式,本身就允许cooike的跨站传递。那我们如何在服务端分辨这个带有合法cookie-sessionid的请求是登录用户真正发出的呢?
A、通过头Referer字段验证
        浏览器请求的header中包含一个Referer字段,这个字段可以获取请求的来源地址。我们可以事先在服务端建立一个访问白名单,如果来源地址不在白名单中,则认为是非法请求。
不过这种方法并非绝对安全,Referer 的值是由浏览器提供的,每个浏览器对于 Referer 的具体实现不尽相同,并不能保证浏览器自身没有安全漏洞。某些浏览器(比如IE系列老版本),存在篡改其Referer字段的可能。攻击者只要将Referer的来源地址改为白名单网站地址,CSRF攻击仍然无法避免。不过现代浏览器基本杜绝了这个安全漏洞,反之存在这个漏洞的浏览器基本绝迹,所以只要网站限制住不安全客户端浏览器的使用,这个方法基本是可行的。
优点:实现简单对原有系统改变较小,尤其是前端代码基本不用改动。
缺点:不能完全保证安全
B、通过在请求地址中添加token字段验证
                token字段一般是一个uuid字符串,可以在用户登录成功后产生并放于 session 之中同时回传给浏览器存储在页面中。在用户后续的请求中在参数中附带这个token,在服务端收到后续请求在拦截器中把 session中的token拿出与请求中的 token 进行比对,一致则表明是合法用户请求。
优点: 由于token字段存在于网站A自身的页面中且每次登录都是随机产生的,攻击者无法直接获取,所以这种方法理论上说是绝对安全的。
缺点:由于需要在浏览器端的所有请求中加上token参数,尤其是以表单提交的代码,前端改版的工作量较大。
网上还有两种方法。
        一种是在 HTTP 头中增加自定义属性并验证,类似请求地址token字段验证,这种方法 只限于ajax提交的方式,表单提交请求无法实现,局限性太大,所以不推荐。
        一种采用图片验证码的方法。由于对原系统改动量太大且对用户很不友好,所以这里就不具体介绍了。
        当然,以上两种方法在特定场合可以作为一种补充方案。
综上所述,最终解决方案:
  1. 服务端将 cookie-session机制 采用类似Header中携带JWT参数鉴权的解决方案,但其也有缺点,具体请自行网上搜索。
  2. 服务端在继续使用cookie-session机制的前提下,如果网站对数据安全要求不是特别高则可以采用A方案,否则采用B方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值