咱媽說別亂點連接之淺談CSRF攻擊

平時常常聽到人們說別亂點連接,當心有病毒。還有長輩們轉發的「天吶~XXX的陰謀,全是病毒」、「XXX驚天大病毒,點了蘋果手機就要爆炸!」、「如今轉發熱門鏈接會亂扣費!千萬別點!」。php

到底長輩們說的這些是對的仍是錯的,是真的仍是假的?下面我用通俗易懂的語言爲你們剖析。html

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進行登陸:cookie

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

paybill.php經過身份驗證後進行扣款,可是必須輸入收款人和扣款金額:session

<?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,進行登陸(實際的用戶登陸更爲複雜,這裏進行簡單模擬了)dom

在瀏覽器訪問paybill.php頁面,轉錢1000元給媽媽。

http://test.com/csrf/paybill.php?money=1000&to_who=mama

黑客網站

這時候黑客發現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攻擊流程:

一、用戶登陸了信任的網站A,而且保存登陸狀態

二、黑客找出網站A沒有防護的連接,經過社會工程學假裝,誘導點擊。

三、只要登陸狀態保持,用戶主動訪問目標連接,則攻擊成功。

有人說那每次訪問其餘網站,把以前的網站都註銷。是的,這個辦法能夠,但這麼作這現實嗎?咱們須要註銷許多經常使用的網站,下次登陸又要輸入用戶名和密碼,極其反人類。這確定不是最佳辦法,防護措施應該讓程序員考慮,用戶別亂點連接是最重要的。

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元給女友):

抓包結果以下,能夠看到Cookie: uid=1money=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,都是讓用戶自身再次確認受權。

這種安全防範的事兒,更應該由程序驗證。

根據驗證是否可靠性思路,能夠有如下幾種方法:

驗證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的一個全局變量,整個網站均可以使用。

總結

CSRF防護原則:

  • GET方式不能用於更新資源的操做

  • POST方式請求加上隨機token驗證

OWASP 2017年的十大安全威脅已經公佈了,咱們能夠看看2013年和2017年CSRF穩穩排在第八位。

總之,CSRF是一種常見的Web安全威脅,它攻擊特色是利用用戶身份信息假裝,發送請求,形成危害。這種攻擊成本極低,但網站和用戶不注意,很容易受到傷害。固然,更使人欣賞的是黑客利用社會工程學欺騙大衆,這纔是最重要的。

若是你們對社會工程學感興趣,推薦一部電影——《我是誰:沒有絕對安全的系統》,很是精彩。

互聯網安全你攻我防,你槍我盾,沒有永遠靈驗的方法,只有學會攻擊,才能抵禦攻擊。

此文已由做者受權騰訊雲技術社區發佈,轉載請註明文章出處

相關文章
相關標籤/搜索