【安全贴士】CSRF攻击与防范

CSRF是Cross Site Request Forgery的缩写,翻译过来就是跨站请求伪造。 也被称为 one click attack/session riding,缩写为:CSRF/XSRF。

你这可以这么理解CSRF攻击:从一个网站A中发起一个到网站B的请求,而这个请求是经过了伪装的, 伪装操作达到的目的就是让请求看起来像是从网站B中发起的, 也就是说,让B网站所在的服务器端误以为该请求是从自己网站发起的,而不是从A网站发起的。当然,请求一般都是恶意的。

要真正理解为什么有CSRF攻击存在,那先了解几个浏览器的跨域访问限制。

Cookie跨域

cookie可以跨二级域名来访问,这个很好理解,例如你在www.cmj.com所在的web应用程序创建了一个cookie, 在cs.cmj.com这样的二级域名对应的应用程序中可以访问,当然你在创建cookie的时候需要指出Domain属性为cmj.com。 其他情况下的不同域名就无法访问这个cookie了。

JavaScript跨域

js跨域是指通过js在不同的域之间进行数据传输或通信,比如用ajax向一个不同的域请求数据, 或者通过js获取页面中不同域的框架中(iframe)的数据。只要协议、域名、端口有任何一个不同,都被当作是不同的域。

通过jsonp跨域

在js中,我们直接用XMLHttpRequest请求不同域上的数据时,是不可以的。 但是,在页面上引入不同域上的js脚本文件却是可以的,jsonp正是利用这个特性来实现的。

如果你的页面使用jquery,那么通过它封装的方法就能很方便的来进行jsonp操作了。 普通的jquery的ajax调用方法基本都采用这个方式,所以就可以调用不同域名实现的API了。

$.getJSON方法会自动判断是否跨域,不跨域的话,就调用普通的ajax方法; 跨域的话,则会以异步加载js文件的形式来调用jsonp的回调函数。

通过document.domain来跨子域

浏览器的同源策略,不能通过ajax的方法去请求不同源中的文档。

我们只要把http://www.example.com/a.htmlhttp://example.com/b.html这两个页面的document.domain都设成相同的域名就可以了。

CSRF攻击详解

CSRF攻击是源于WEB的隐式身份验证机制!WEB的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的!

A网站访问B网站服务器的一些需要认证的资源的时候,如果没有Cookie信息,服务器是拒绝访问的,那么A网站就无法进行恶意操作。 而伪造成B网站的请求,就可以将B网站的Cookie一起发到B服务器,这个时候就服务器就认为该请求是合法的,就会给出正确的响应, 这个时候,A网站就达到目的了。

通俗点的例子

假设有一个微博网站B,B有一个”加关注”的功能,即一个用户可以关注另一个用户, 而这个功能是这样的实现的:用户每次点击”加关注”按钮的时候,就会向服务器发送一个GET请求,URL如下:

1
http://www.bdomain.com/addfriends?uid=123

URL中的参数uid表示的是你要关注的用户的ID。

在正常情况下,即你登录B网站后,点击”加关注”按钮,浏览器会将上面的URL连同B网站产生的Cookie一起发送到B网站的服务器, B服务器首先通过Cookie进行用户认证,如果Cookie中的信息正确,就会进行向数据库中写入记录,这样,你就成功关注了ID为123的用户。

假如我是一个恶意用户,我想让更多的人关注我,而我又不想通过正常的渠道去实现,因为毕竟正常渠道比较浪费时间, 于是我便开始想歪主意,碰巧,B网站的”加关注”的实现原理被我发现了。这个时候,我便进行了如下操作:

首先我在我自己的网站A里发了篇文章,文章中全是妖娆的美女图片,大家都喜欢美女嘛,所以就会有很多人来看我的这些美女, 我们知道,图片在网页中大都是通过<img scr="http://xxxx/xx.png"/>这样的形式实现的,图片加载的时候就会请求src中指定的URL, 而我就把众多美女图片的src写成了B博客里”加关注”的URL,不同的是将参数uid的值都写成了我在B网站中的ID(假设是456),即:

1
http://www.bdomain.com/addfriends?uid=456

浏览器一看请求的域名是bdomain.com,便将存放在客户端的B网站的Cookie给顺带一起发了过去。

借用别人的图文来说明一个整个CSRF的过程:

从上图可以看出,要完成一次CSRF攻击,受害者必须依次完成两个步骤:

  1. 登录受信任网站A,并在本地生成Cookie。
  2. 在不登出A的情况下,访问危险网站B。

CSRF的防御

防御效果是从服务端着手效果比较好,现在一般的CSRF防御也都在服务端进行。

所有表单都包含同一个伪随机值,这可能是最简单的解决方案了, 因为攻击者不能获得第三方的Cookie(理论上),所以表单中的数据也就构造失败了。

这个方法个人觉得已经可以杜绝99%的CSRF攻击了,那还有1%呢….由于用户的Cookie很容易由于网站的XSS漏洞而被盗取,这就另外的1%。

验证码

每次的用户提交都需要用户在表单中填写一个图片上的随机字符串,这个方案可以完全解决CSRF,但个人觉得在易用性方面似乎不是太好。