CSRF

        CSRF 全称是 Cross Site Request Forgery,即跨站点请求伪造。其主要原理是,利用用户的身份操作用户帐户。

        具体的操作方式包括显示攻击和隐式攻击。前者是将 CSRF 的 URL 贴在论坛等地,并诱使有执行权限的管理员点击,该方式对于用户是可察觉的;后者可以通过在自己的域构造一个页面,精心配置 <img>、<iframe>、<script>、<link> 等标签的 src 字段,指向 CSRF 的 URL,只要管理员访问了 Hacker 域的页面,即使没有直接点击 CSRF 的 URL,也能发起对其它站点的请求从而完成 CSRF。

一. 原理

1. 浏览器的 Cookie 策略

        浏览器持有的 Cookie 分为 2 种:一种是 Session Cookie,又称临时 Cookie;另一种是 Third-party Cookie,也称本地 Cookie。

        两者的区别在于是否设置 Expire 时间。前者没有指定 Expire 时间,保存在浏览器的进程空间内,在浏览器关闭后,Session Cookie 就失效了。后者指定了 Expire 时间,只有到了 Expire 时间后 Cookie 才会失效,所以 Third-party Cookie 会保存在本地。

        如果浏览器从一个域的页面中,要加载另一个域的资源,对于是否允许加载 Third-party Cookie,各个浏览器的策略是不一样的。在当前主流的浏览器中,默认会拦截 Third-party Cookie 的有:IE6、IE7、IE8、Safari,不会拦截的有:FireFox2、FireFox3、Opera、Google Chrome、Android。

        浏览器拦截 Third-party Cookie 的发送,在某种程度上降低了 CSRF 的威力,因为大部分敏感或者重要的操作都是隐藏在认证之后的。但是,拦截 Third-party Cookie 并不意味着万事大吉。对于 IE 浏览器,攻击者需要精心构造攻击环境,比如诱使用户在当前浏览器中先访问目标站点,使得 Session Cookie 生效,那么仍然可以实施 CSRF,即,几乎所有的浏览器,都不会拦截 Session Cookie

2. P3P 头

        P3P 头是 W3C 制定的一项关于隐私的标准,全称是 The Platform for Privacy Preferences。

        如果网站返回给浏览器的 HTTP 头中包含 P3P 头,则在某种程度上说,将允许浏览器发送 Third-party Cookie。在 IE 下即使是 <iframe>、<script> 等标签也将不再拦截 Third-party Cookie 的发送。

            HTTP/1.1 200 OK

            P3P: CP=CURa ADMa DEVa PSAa PSDo OUR BUS UNI PUR INT DEM......

            Content-Type: text/html

            ......

        此外,P3P 头也可以直接引用一个 XML 策略文件:

            ......

            P3P: policyref="http://xxxxxx/P3P/PolicyReferences.xml"

            ......

        更多关于 P3P 的内容,请参考 W3C 标准:www.w3.org/TR/P3P

        在网站的业务中,P3P 头主要用于类似广告等需要跨域访问的页面。但是很遗憾的是,P3P 头设置后,对于 Cookie 的影响将扩大到整个域中的所有页面,因为 Cookie 是以域和 path 为单位的,这并不符合“最小权限”原则。

        例如,访问 www.b.com,页面是一个 <iframe>,会发起对 www.a.com/test.php 的访问,而 www.a.com/test.php 会对 www.a.com 域设置 Cookie。此时,由于跨域设置,在 www.a.com 上 Set-Cookie 是不会成功的。但是设置了 P3P 头之后,则可以 Set 成功。

        正因为 P3P 头目前在网站中被广泛应用,因此在防御 CSRF 时不能依赖于浏览器对 Third-party Cookie 的拦截策略。而且,很多时候,如果测试 CSRF 时发现 <iframe> 等标签在 IE 中居然能发送 Cookie,而又找不到原因,那么很有可能就是 P3P 头在作怪。

3. GET/POST 都可 CSRF

        在 CSRF 攻击流行之初,曾经有一种错误的观点,认为 CSRF 攻击只能由 GET 请求发起。因此很多开发者都认为只要把重要的操作改成只允许 POST 请求,就能防止 CSRF 攻击。

        这种错误的观点形成的原因主要在于,大多数 CSRF 攻击发起时,使用的 HTML 标签都是 <img>、<iframe>、<script> 等带 "src" 属性的标签,这类标签只能够发起一次 GET 请求,而不能发起 POST 请求。

        如果服务器端已经区分了 GET 与 POST,对于攻击者来说,有若干种方法可以构造出一个 POST 请求。最简单的方法,就是在一个页面中构造好一个 form 表单,然后使用 JavaScript 自动提交这个表单。比如:

            <form action="http://www.a.com/register" id="register" method="post">

                <input type=text name="username" value="" />

                <input type=password name="password" value="" />

                <input type=submit name="submit" value="submit" />

            </form>

            <script>

                var f = document.getElementById("register");

                f.inputs[0].value = "test";

                f.inputs[0].value = "passwd";

                f.submit();

            </script>

4. Flash CSRF

        Flash 也有多种方式能够发起网络请求,包括 GET 和 POST,比如 URLRequest、getURL、loadVars 等。在 IE6、IE7 中,Flash 发送的网络请求均可带上 Third-party Cookie,但是从 IE8 起,Flash 发起的网络请求已经不再发送 Third-party Cookie 了。


二. 防御

1. 验证码

        虽然验证码能够很好的遏制 CSRF,但是考虑到用户体验,网站不能给所有的操作都加上验证码。因此,验证码只能作为辅助手段。

2. Referer Check

        虽然页面与页面之间一般都具有一定的逻辑关系,但是,服务器并非任何时候都能取到 Referer。很多用于出于隐私保护的考虑,限制了 Referer 的发送。在某些情况下,浏览器也不会发送 Referer,比如从 HTTPS 跳转到 HTTP,出于安全的考虑,浏览器也不会发送 Referer。因此,无法依赖 Referer Check 作为防御 CSRF 的主要手段,但可用于监控 CSRF 的发生。

3. 参数加密

        CSRF 能够成功的本质原因,是所有的参数都可以被预测。因此,如果对重要参数加密,就可以让攻击者猜不到参数。

        例如,一个删除操作的 URL 是:

            http://host/path/delete?username=weiyuzhang&item=123

        把其中的 username 参数改成哈希值:

            http://host/path/delete?username=md5(salt+weiyuzhang)&item=123

        这样,在攻击者不知道 salt 的情况下,是无法构造该 URL 的。而服务器则可以从 Session 或者 Cookie 中取得 username=weiyuzhang 的值,然后结合 salt 来验证请求,从而确定请求是否正常。

        该方案的缺点是,URL 难读,会影响日志分析,而且,如果 salt 发生变更,则用户的收藏就失效了。

4. Anti CSRF Token

        以上面的 URL 为例,保持原参数不变,新增一个参数 Token,该 Token 随机:

            http://host/path/delete?username=weiyuzhang&item=123&token=[random(seed)]

        Token 必须足够随机,且应该作为一个“秘密”,为用户与服务器共同持有,不能被第三方知晓。在实际应用中,Token 可以放在用户的 Session 中,或者浏览器的 Cookie 中。由于 Token 的存在,攻击者便无法再构造一个完整的 URL 实施 CSRF 攻击。

        Token 需要同时放在表单和 Session 中。在提交请求时,服务器只需验证表单中的 Token 与用户 Session 或者 Cookie 中的 Token 是否一致,只有一致时才认为是合法请求。

        如这个表单,Token 作为一个隐藏的 input 字段,放在 form 中:

            <form id="itemsForm" target="_blank" name="itemsForm" method="post">

                <input type="hidden" value="xxxxxxxx" name="_tb_token">

        Token 的使用,有如下注意事项:

        1. Token 的生成一定要足够随机,需要使用安全的随机数生成器生成 Token;

        2. 该 Token 不是为了防止重复提交,因此,在 Token 消耗掉之前,都可以使用同一个 Token,但如果用户已经提交了表单,则该 Token 已经被消耗,应该再次重新生成一个新的 Token;

        3. 如果 Token 保存在 Cookie 中,而不是服务器端的 Session 中,会带来的问题就是,如果一个用户打开几个相同的页面同时操作,但某个页面消耗掉 Token 后,其它页面的表单内保存的还是被消耗掉的那个 Token,因此其它表单提交时会出错。这种情况下,可以考虑生成多个有效的 Token,以解决多页面共存的场景;

        4. 需要注意 Token 的泄漏。比如以下页面:

            http://host/path/manage?username=abc&token=[random]

        如果该页面包含了一张攻击者能够指定地址的图片:

            <img src="http://evil.com/notexist" />

        则 "http://host/path/manage?username=abc&token=[random]" 会作为 HTTP 请求的 Referer 发送到 evil.com 的服务器上,导致泄漏。

        因此,在使用 Token 时,应该尽量把 Token 放在表单中,把敏感操作由 GET 改为 POST,以 form 表单或者 AJAX 的形式提交,可以避免 Token 泄漏。

        对于在 form 中放入 Token,一般需要前端和框架配合完成。前端在 form 中预留相关字段,框架在渲染页面时,填充该字段。把 Token 放入 Cookie 中也好办。但是如何把 Token 放入 Session 中呢?或者说,如果浏览器不支持 Cookie,我们一般是怎么做的?

        另外,CSRF 的 Token 仅仅用于对抗 CSRF 攻击,当网站还同时存在 XSS 漏洞时,这个方案就会无效,因为 XSS 可以模拟客户端浏览器执行任意操作。在 XSS 攻击下,攻击者完全可以请求页面后,读出页面里的 Token 值,然后再构造出一个合法的请求。这个过程称之为 XSRF。???


三. 漏洞扫描

1. Rational AppScan

        基本原理是,通过对同一个需要检测的 URL 或者 Service 按照顺序发出两次请求,发送两次请求之间会做一次退出登陆的操作,如果一个对 CSRF 已经防范的网站是会发送回两个不同的回复内容的。

        PS:这是不是已经 out 了啊。大部分敏感操作都是隐藏在认证之后,退出登陆后,当然不能成功执行哇

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章