咱妈说别乱点链接是对的,有毒

平时经常听到人们说别乱点链接,小心有病毒。还有长辈们转发的“天呐~XXX的阴谋,全是病毒”、“XXX惊天大病毒,点了苹果手机就要爆炸!”、“现在转发热门连接会乱扣费!千万别点!”。

到底长辈们说的这些是对的还是错的,是真的还是假的?下面我用通俗易懂的语言为大家剖析。

CSRF攻击就是假装你去行骗

首先我们说说CSRF(Cross-site request forgery),它的中文名称是跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。

简单地说,CSRF就是利用了我们的登录状态或者授权状态(请注意“利用”,并没有窃取到),然后做一些损害我们自身利益的事情。

举个例子,CSRF就是使用我们微信头像和昵称,然后去跟咱爸妈要钱。“爸,生活费不够了。你打到XXX账户上”,爸爸看见头像和昵称以为是亲生的就转账了。如此,行骗成功。

GET方式造成的CSRF攻击

长辈们说得对,链接确实不能随便点。我用简单的代码举个例子。

信任的网站test.com

假设我们有一个银行账户,其中有一个登录页面login.php和付款页面paybill.php,这些页面都属于我们信任的网站test.com(test.com网站是虚拟的)。

在login.php中设置cookie进行登录:

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

在paybill.php通过身份验证后进行扣款,但是必须输入收款人和扣款金额:

<?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,进行登录(实际的用户登录更为复杂,这里简化了)

登录信任的网站test.com

在浏览器访问paybill.php页面,转钱1000元给妈妈。
http://test.com/csrf/paybill.php?money=1000&to_who=mama

转账1000元给妈妈

黑客网站

这时候黑客发现test.com网站没有做任何防御措施,他立刻在自己的网站B上伪造了一个页面,页面上有这么一个链接,他的收款人to_who变成了hacker。

<html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <a href="http://test.com/csrf/paybill.php?money=1000&to_who=hacker" taget="_blank">震惊!!史上尺度最大的照片!!<a/>
    </body> 
</html>

假设因为好奇心点击了该连接:


伪造的链接

这样就转了1000元给了hacker


为什么会出现这种情况,我们在别的网站点击链接居然能扣自己账户的钱?点击链接前,我们已经登录了信任网站test.com,而这个test.com/csrf/paybill.php?money=1000&to_who=hacker这个连接是我们自己发送的,test.com会识别当前已经登录,然后转账,test.com网站无法判断到底是谁让我们点击的。

从上面这个实例可知,完成CSRF攻击流程:
1、用户登录了信任的网站A,并且保存登录状态。
2、黑客找出网站A没有防御的链接,通过社会工程学伪装,诱导点击。
3、只要登录状态保持,用户主动访问目标链接,则攻击成功。

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

CSRF的攻击渠道不一定来自其他网站,也可以是广告邮件、QQ空间、微信、facebook等社交媒体或软件。试想一下,如果你的女朋友知道这个链接,她在QQ上发给你:

http://test.com/csrf/paybill.php?money=1000&to_who=girlfriend你点击后,那就转了1000元给女朋友,假设她将money改成10w,后果真的不敢想象,你居然存了这么私房钱,跪搓衣板吧,钱也都到了你女朋友账户上。

好了,小白用户看见这里可以关闭,别乱点链接就对,该给女朋友的钱的还是一分不能少。

POST也能造成CSRF攻击

上面的CSRF可以说相当危险,更新资源的操作不应该使用GET方式,GET方式只应该用于读操作。更新操作一定要使用POST方式,特别涉及到钱的问题。

然而POST方式可以解决大部分的CSRF问题,还有剩下少部分的聪明的黑客,一样能够模拟POST请求,伪造身份进行攻击。

假设paybill.php 我们修改为POST取:

<?php
    if (!isset($_COOKIE['uid'])) {
        die('login error!');
    }   
    if (!isset($_POST['money'])) {
        die('no money');
    }   
    if (!isset($_POST['to_who'])) {
        die('nobody');
    }   
    $uid = $_COOKIE['uid']; 
    $money = $_POST['money'];
    $to_who = $_POST['to_who'];
    if ($uid > 0) {
        echo "transfer {$money} yuan to {$to_who}!";
    }   

访问http://test.com/csrf/paybill.php?money=1000&to_who=girlfriend,提示没有money

但是道高一尺魔高一丈,聪明的黑客也改进了代码,使用POST提交,并且money改为2000:

<html>
    <head>
        <meta charset="utf-8">
    </head>
    <body>
        <form action="http://test.com/csrf/paybill.php" method="post">
            <input type="hidden" name="money" value="2000">
            <input type="hidden" name="to_who" value="girlfriend">
            <input type="submit" value="点击中大奖">
        </form>
    </body> 
</html>

点击中大奖,如下(又转了2000元给女朋友):

转账2000给女朋友

抓包结果如下,可以看到Cookie: uid=1和money=2000&to_who=girlfriend都已发送过去。

POST http://test.com/csrf/paybill.php HTTP/1.1
Host: test.com
Referer: http://hack.com/hack/welcome.php
Cookie: uid=1

money=2000&to_who=girlfriend

如此一来,不管哪种访问方式都可能受到攻击。所以,这并不是GET和POST谁更安全的问题,POST只是提高了攻击门槛和成本(其实也就多几行html和js)。

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

防御CSRF的思想

那么防御的方法有很多:
1、比如加上验证码。但这么做很繁琐,并且影响用户体验。
2、比如转账需要二次密码验证,现在很多银行就这么搞的。
3、确认来源是否可靠(推荐)

不管防御方法1还是2,都是让用户自身再次确认授权。
这种安全防范的事儿,更应该由程序验证。

根据验证是否可靠性思路,可以有以下几种方法:

  • 验证HTTP Referer 字段

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

从我们的网站访问paybill.php,抓包发现Referer是不存在的

"HTTP_REFERER"=>""

从黑客的网站访问paybill.php,抓包发现Referer来自黑客网站

["HTTP_REFERER"]=> string(35) "http://hack.com/welcome.php"

然后代码里判断:

if  (HTTP_REFERER!="")  {
    die('可能是CSRF攻击,拒绝访问');
} else {
    die('允许访问');
}

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

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

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

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

新增form.php表单页面,将token存入session(不要存在cookie中,你懂的):

<?php
session_start();
$csrf_token = md5(openssl_random_pseudo_bytes(32));//生成随机token
$_SESSION['token']= $csrf_token;
?>
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>csrf</title>
</head>
<body>
    <h1>转账</h1>
    <form action="paybill.php" method="POST">
        money:<input type="text" name="money">
        to_who:<input type="text" name="to_who">
        <input type="hidden" name="token" value="<?php echo $csrf_token ?>">
        <input type="submit" value="ok">
    </form>
</body>
</html>

在paybill.php获取token,与session中存储的token判断是否一致:

<?php
    session_start();
    if (! isset($_COOKIE['uid'])) {
        die('login error!');
    }   
    if (! isset($_POST['money'])) {
        die('no money');
    }   
    if (! isset($_POST['to_who'])) {
        die('nobody');
    }   
    if (! isset($_POST['token']) || $_POST['token'] !=  $_SESSION['token']) {
        die('forbidden');
    }   
    $uid = $_COOKIE['uid']; 
    $money = $_POST['money'];
    $to_who = $_POST['to_who'];

    if ($uid > 0) {
        echo "transfer {$money} yuan to {$to_who}!";
    }   

查看页面form.php:

请求成功:


每次访问表单页面,都应该生成一个token:

<input type="hidden" name="token" value="a88f67a7effa917450cff12e179df35d">

我们再尝试从黑客网站进行访问,显示"forbidden",证明在token验证时被拦截:

这样子就已经有效防御csrf攻击。该方法可以用于a链接和表单等请求,属于同一个原理。

注意:网上很多文章并没有生成唯一的或者随机性较大的token,都是同一个token,这是有问题的,如果黑客看到该token,一样可以伪造请求,进行攻击。

Ajax防御CSRF

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

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

它是这么说的:

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

个人觉得不需要如此麻烦,易用性也不太好,直接对Aajx进行一次封装,加入一个open_token的选项,true就把token也发送过去,否则不进行验证,原理和上面是一样的。

最好将token赋值给js的一个全局变量,整个网站都可以使用。

总结

OWASP 2017年的十大安全威胁已经公布了,我们可以看看2013年和2017年CSRF稳稳排在第八位。


OWASP

总之,CSRF是一种常见的Web安全威胁,它攻击特点是利用用户身份信息伪装,发送请求,造成危害。这种攻击成本极低,但网站和用户不注意,很容易受到伤害。当然,更令人欣赏的是黑客利用社会工程学欺骗大众,这才是最重要的。

如果大家对社会工程学感兴趣,推荐一部电影——《我是谁:没有绝对安全的系统》,非常精彩。

没有绝对安全的系统

互联网安全你攻我防,你枪我盾,没有永远灵验的方法,只有学会攻击,才能抵御攻击。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容