程序员的知识教程库

网站首页 > 教程分享 正文

CSRF原理及解决方案 csrf工作原理

henian88 2024-12-25 11:59:40 教程分享 7 ℃ 0 评论

CSRF详解

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

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

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

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

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

这里面又有几个问题:

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

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

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

防止CSRF


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

    1. 增加referer验证
    2. 增加token防护
    3. 使用验证码

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

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

好,来考虑一个验证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','https://account.xiaomi.com')
 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.1
Host: account.xiaomi.com
Connection: close
Origin: http://a.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36
Accept: */*
Referer: http://a.com/csrf/proxy.html
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8


可以看到referer就是proxy.html发起的,这样就把referer限制死了
当然针对其他方法也是可以的,ajax是可以发起各种请求的
其他域名的请求,只要增加这样一个iframe,调用这个proxy.html文件就能实现get验证referer了。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表