龙空技术网

CSRF原理及解决方案

IT随笔 147

前言:

如今你们对“ajaxcsrf”都比较讲究,姐妹们都想要学习一些“ajaxcsrf”的相关资讯。那么小编也在网摘上汇集了一些有关“ajaxcsrf””的相关内容,希望小伙伴们能喜欢,同学们快快来了解一下吧!

CSRF详解

CSRF大家都知道是跨站请求伪造,说是带有跨站两字,其实并没有真正产生绕过同源策略。来看下主要的攻击步骤,首先诱使用户登入目标网站,然后在诱使受害者访问攻击者设置好的站点,利用目标网站对用户的信任,来达到使受害者不知情的情况下完成某项操作的请求

具体来分析下里面一个很关键的点:

需要用户登入目标站点,为啥必须在登入的情况下才能实施?利用目标网站对用户的信任,为啥目标网站会知道该请求是信任用户发起的?

这两个很关键的点本身就是利用的同源策略中的cookie策略

cookie里面的几个基本属性很多人不知道,只是知道cookie是用作认证用的,客户端把cookie传递给服务器端,服务器端根据cookie里面的值来做认证判断。

这里面又有几个问题:

cookie从客户端传递到服务器端,那些cookie是需要传递的,那些是不需要的呢?cookie是不是可以根据不同的path传递不同的值?为啥有的站点在退出浏览器后就需要重新登入,有的站点是不需要的?

下面来一一解决这些问题:

为啥需要用户登入目标站点,这个就结合cookie来解释,cookie中的一个基本属性是domain,这个domain来决定那些cookie是需要随着请求带到服务器端做认证。要想利用csrf攻击成功,需要用户登入,也是为了获取到他在这个domain下面的认证cookie,这样构造的csrf页面请求操作时候才会有cookie带上目标为啥信任用户请求,由于服务器端是根据客户端来的请求带的cookie来做用户的判断,服务器不知道人是谁,我只是知道你给我的我认就好了,你传递过来的cookie没有过期,而且可以认证通过,我为啥不信任。cookie的几个基本属性是保证cookie作用的,一个是domain,是访问那些domain的时候会带那些cookie去服务器。一个path:这个大家关注的少,是保证在访问不同的path时候带那些cookie,以前遇到的一个案例,get请求中可以传递给服务器setcookie并且可以控制属性,那么可以构造一个不同路径的path cookie写入受害者的浏览器下,举个小例子,一个商城站点,在登入的时候会有个setcookie,登入站点的时候是受害者的账户,但是攻击者写个例如cart和pay自路径下面的认证cookie,受害者在支付的时候调用pay自路径页面,此时支付成功,但是支付的时候你用的是我的cookie认证,那么你支付的是我cart下面的订单,用你的钱买了东西,这个有个比较详细的研究文档,忘记在哪了cookie还有几个和安全比较相关的属性,httponly和secure,httponly是保证xss等js文件无法读取到该cookie,secure这个大家知道,但是很少会遇到安全漏洞,当时出现了一个黑产案例,很多人反映自己的账号下莫名多了订单,我们挖漏洞,扫描都没有发现安全漏洞,用户账号也不是弱密码,风控也没有问题,这个就是当时忽略了一个点,抓包的时候passtoken没有加secure属性,当时账号主站是https的,没问题,但是有个子站点是走的http,这样没有加secure属性,又是同一个域下面,passtoken传递到了http的站点下面,并且可以传输出去,导致了token的泄露cookie的另外小属性:过期时间expires/Max-Age防止CSRF

很多情况下防止CSRF大家都知道几个解决方案:

增加referer验证增加token防护使用验证码

来说这几个方案的优缺点:

referer是最简单的,但是针对于get请求怎么防护,限制只有几个站点才能发起get请求,不现实token最有效,但是也是最重的,需要服务器端针对每个用户生成不同的token,并且需要缓存记录,用户带token的请求过来需要验证,验证完毕后失效掉,清除缓存,这个很重,对于一个小业务来说,没必要这样做验证码也挺有效,需要输入验证后才能发起请求,但是用户体验不好,每次操作时候输入个验证码,用户会疯的

好,来考虑一个验证referer的方案,来解决get请求的防御,首先看方案的一个代码实现:

<iframe id="proxy" name="proxy" frameborder="no" src="proxy.html" onload=xss()></iframe><script> document.domain = "a.com" function xss(){ ajax = proxy.xmlHttp() ajax.open('get',';) ajax.send(); ajax.onreadystatechange = function () { if (ajax.readyState == 4 && ajax.status == 200) { console.log('send');            }        }    }</script>

其中proxy.html的代码如下:

<html><head> <script> document.domain = 'a.com'; xmlHttp = (function () { var f; if (window.ActiveXObject) { f = function () { return new ActiveXObject('Microsoft.XMLHTTP'); };            } else if (window.XMLHttpRequest) { f = function () { return new XMLHttpRequest(); };            } else { f = function () { return; };            } return f;        })(); </script></head><body></body></html>

抓包看最后的发起请求内容如下:

GET / HTTP/1.1Host: account.xiaomi.comConnection: closeOrigin: : Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36Accept: */*Referer: : gzip, deflateAccept-Language: zh-CN,zh;q=0.9,en;q=0.8

可以看到referer就是proxy.html发起的,这样就把referer限制死了

当然针对其他方法也是可以的,ajax是可以发起各种请求的

其他域名的请求,只要增加这样一个iframe,调用这个proxy.html文件就能实现get验证referer了。

标签: #ajaxcsrf