读完就带你入门CSRF

前言:不想读完的,可以理解这一句话,“如果你能理解XSS攻击的话,那么你一定认同XSS攻击是利用盗取的高权限cookie来进行的,相较而言CSRF并不干偷盗之事,而是借刀杀人”,这是我自己的感想,不认同的也不强求


CSRF(cross-site request forgery),跨站请求伪造,算了算了,概念性的我就不多说了,都能百度到,下面我直接说重点吧。

CSRF的攻击原理是什么,简单说就是利用了高权限帐号(如管理员)的登录状态或者授权状态去做一些后台操作,但实际这些状态并没有被我们直接获取到(获取那是XSS干的事)。

GET方式演示CSRF攻击

我们这里假设有个网站test.com,包含一个登录页面(login.php)和一个付款页面(pay.php)

login.php:

1
2
3
<?php
setcookie('uid', 1, time()+86400);
echo "your uid is {$_COOKIE['uid']}";

pay.php:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
//身份验证
if (!isset($_COOKIE['uid']) || $_COOKIE['uid']< 0) {
die('login error!');
}
//金额获取
if (!isset($_GET['money'])) {
die('no money');
}
//收款人获取
if (!isset($_GET['to_who'])) {
die('nobody');
}
$uid = $_COOKIE['uid'];
$money = $_GET['money'];
$to_who = $_GET['to_who'];
//。。。。。。
//此处应该还有相关DB操作,略去
echo "transfer {$money} yuan to {$to_who}!";

打开login.php,模拟登录,可以看到登录成功:

1
your uid is 1

登录完成后我们打开pay.php进行转账,转1000元给father,GET请求构造:http://test.com/pay.php?money=1000&to_who=father

访问得到转账成功的响应:

1
transfer 1000 yuan to father!

以上是用户正常操作,这时黑客发现了该网站的CSRF漏洞,于是立刻伪造了一个页面,页面上预置了一个UC震惊部的超链接,超链接指向http://test.com/pay.php?money=1000&to_who=hacker

1
2
3
4
5
6
7
8
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<a href="http://test.com/pay.php?money=1000&to_who=hacker" taget="_blank">震惊!男人看了会沉默,女人看了会流泪!不转不是中国人!<a/>
</body>
</html>

如上,收款人被篡改成了黑客,如果我们是中国人,就一定会毫不犹豫的点击链接,因为不转不是中国人!!然后,然后。。。我们就把自己账户的1000元开心的转给了黑客。

为什么会出现这种情况,我们在别的网站点击链接居然能扣自己账户的钱?

点击链接前,我们已经登录了信任网站test.com,而这个这个链接是我们自己发送的,test.com会识别当前已经登录,然后转账,网站无法判断到底是谁让我们点击的。

从上面这个实例可知,完成CSRF攻击流程:

1、用户登录了信任的网站A,并且保存登录状态

2、黑客找出网站A没有防御的链接,通过社会工程学伪装,诱导点击。

3、只要登录状态保持,用户主动访问目标链接,则攻击成功。

有人说那每次访问其他网站,把之前的网站都注销。是的,这个办法可以,但这么做这现实吗?我们需要注销许多常用的网站,下次登录又要输入用户名和密码,极其反人类。这肯定不是最佳办法,防御措施应该让程序员考虑,用户别乱点链接是最重要的。


以上演示了GET方式攻击,有人说那我把method改为POST吧,其实稍微动动脑也知道不行,POST请求我们同样可以伪造,隐藏表单的方式我们见多不怪了,所以仅仅依靠简单的POST传输依旧无济于事。

如此一来,不管哪种访问方式都可能受到攻击。所以,这并不是GET和POST谁更安全的问题,POST只是提高了攻击门槛和成本。

划重点,那么CSRF能够攻击的根本原因是:服务器无法识别你的来源是否可靠


最后,我们聊一聊前辈是如何防御CSRF攻击的:

防御的方法有很多:

1、比如加上验证码。但这么做很繁琐,并且影响用户体验。

2、比如转账需要二次密码验证,现在很多银行就这么搞的。

3、确认来源是否可靠(推荐)

1和2都是同一个思路,那就是验证请求合法性,从这一思路出发,前辈想出了下面几种方法:

验证HTTP Referer 字段

HTTP协议里面定义了一个访问来源的字段,这个字段叫Referer。黑客伪造的链接或表单是在其他网站上,所以我们可以判断Referer是否为自身网站,如果是,则允许访问,如果不是,则拒绝访问。

但是这种方法是有缺陷的,上面实验尝试过,如果对方在QQ上发送给你一个链接呢?点击的时候属于主动点击,此时一样没有Referer。程序会把它归属为安全请求,那么就被绕过了。并且如果某些低版本的浏览器存在漏洞(比如IE6),Referer很有可能被篡改,所以这个方法并非十全十美。

服务端验证请求的token一致性

CSRF攻击的核心原理就是利用用户验证信息储存cookie中,发送请求,使得服务器无法判断真伪,而token之所以能够拦截,就是因为它是CSRF攻击过程中几乎不可能伪造的东西。

实现原理:在服务端生成一个随机的token,加入到HTTP请求参数中,服务器拦截请求,查看发送的token和服务端的是否一致,若一致,则允许请求;若不一致,则拒绝请求。

注意:一定要生成唯一的或者随机性较大的token。如果token可以被爆破,一样可以伪造请求,进行攻击。

参考文章:https://blog.csdn.net/li741350149/article/details/62887524

Ajax防御CSRF

实际上Ajax防御的思想也可以利用上面的token验证方式。

IBM上一篇文章说Ajax防御时,在 HTTP 头中自定义属性并验证token。

它是这么说的:

1
把 token 以参数的形式置于 HTTP 请求之中,而是把它放到 HTTP 头中自定义的属性里。通过 XMLHttpRequest 这个类,可以一次性给所有该类请求加上 csrftoken 这个 HTTP 头属性,并把 token 值放入其中。这样解决了上种方法在请求中加入 token 的不便,同时,通过 XMLHttpRequest 请求的地址不会被记录到浏览器的地址栏,也不用担心 token 会透过 Referer 泄露到其他网站中去。

总结

CSRF防御原则:

  • GET方式不能用于更新资源的操作
  • POST方式请求加上随机token验证
文章作者: Qug_
文章链接: https://www.qugcloud.cn/2018/04/19/csrf-details/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Qug's Blog