本文作者:心月

CSRF攻擊案例解析以及防御策略

心月IT博客 2019-03-21
摘要:CSRF(Cross-site request forgery)跨站請求偽造,也被稱為“One Click Attack”或者Session Riding,是一種對網站的惡意利用。盡管聽起來像跨站腳本(XSS),但它與XSS非常不同,XSS利用站點內的信任用戶,而CSRF則通過偽裝成受信任用戶的請求來利用受信任的網站。與XSS攻擊相比,CSRF攻擊往往不大流行(因此對其進行防范的資源也相當稀少)和難以防范,所以被認為比XSS更具危險性。

一、什么是CSRF攻擊

    CSRF(Cross-site request forgery)跨站請求偽造,也被稱為“One Click Attack”或者Session Riding,通常縮寫為CSRF或者XSRF,是一種對網站的惡意利用。盡管聽起來像跨站腳本(XSS),但它與XSS非常不同,XSS利用站點內的信任用戶,而CSRF則通過偽裝成受信任用戶的請求來利用受信任的網站。與XSS攻擊相比,CSRF攻擊往往不大流行(因此對其進行防范的資源也相當稀少)和難以防范,所以被認為比XSS更具危險性。


二、CSRF攻擊原理

CSRF攻擊原理圖

CSRF攻擊原理

通過上圖分析我們可以知道構成CSRF攻擊是有條件的:

  1、客戶端必須一個網站并生成cookie憑證存儲在瀏覽器中

  2、該cookie沒有清除,客戶端又tab一個頁面進行訪問別的網站


三、CSRF攻擊案例解析

以網絡游戲虛擬轉賬為此次的CSRF攻擊案例。

1、簡單級別的CSRF攻擊

假設某游戲網站的虛擬幣轉賬是采用GET方式進行操作的,樣式如:

http://www.game.com/Transfer.php?toUserId=11&vMoney=1000

    此時惡意攻擊者的網站也構建一個相似的鏈接:

  ① 可以是采用圖片隱藏,頁面一打開就自動進行訪問第三方文章:<img src='攻擊鏈接'>

  ② 也可以采用js進行相應的操作

模擬攻擊鏈接:

http://www.game.com/Transfer.php?toUserId=20&vMoney=1000         #toUserID為攻擊的賬號ID
1、假若客戶端已經驗證并登陸www.game.com網站,此時客戶端瀏覽器保存了游戲網站的驗證cookie
2、客戶端再tab另一個頁面進行訪問惡意攻擊者的網站,并從惡意攻擊者的網站構造的鏈接來訪問游戲網站
3、瀏覽器將會攜帶該游戲網站的cookie進行訪問,刷一下就沒了1000游戲虛擬幣

2、中等級別的CSRF攻擊

    游戲網站負責人認識到了有被攻擊的漏洞,將進行升級改進。

  將由鏈接GET提交數據改成了表單POST提交數據

//提交數據表單
<form action="./Transfer.php" method="POST">
    <p>toUserId: <input type="text" name="toUserId" /</p>
    <p>vMoney: <input type="text" name="vMoney" /></p>
    <p><input type="submit" value="Transfer" /></p>
</form>

Transfer.php接收數據處理頁面

<?php
     session_start();
     if (isset($_REQUEST['toUserId'] && isset($_REQUEST['vMoney']))  #驗證
     {
          //相應的轉賬操作
     }
 ?>

    惡意攻擊者將會觀察網站的表單形式,并進行相應的測試。

  首先惡意攻擊者采用(http://www.game.com/Transfer.php?toUserId=20&vMoney=1000)進行測試,發現仍然可以轉賬。 

  那么此時游戲網站所做的更改沒起到任何的防范作用,惡意攻擊者只需要像上面那樣進行攻擊即可達到目的。

  總結:

  網站開發者的錯誤點在于雖然升級了數據的提交方式,但數據接收的方式并沒有升級,我們都知道request既可以接收get提交的數據,同時也可以接收post提交的數據。用$_REQUEST接收POST和GET發來的數據,因此漏洞就產生了。

3、高級別的CSRF攻擊

    后來游戲網站開發者又再一次認識到了錯誤,將進行下一步的改進與升級,將采用POST來接收數據

Transfer.php數據請求接收處理頁面

<?php
     session_start();
     if (isset($_POST['toUserId'] && isset($_POST['vMoney']))  #驗證
     {
          //相應的轉賬操作
     }
 ?>

    此時如果在用之前的鏈接攻擊就會被攔截。但這樣惡意攻擊者就沒有辦法進行攻擊了么?那是不可能的。

  惡意攻擊者根據游戲虛擬幣轉賬表單進行偽造了一份一模一樣的轉賬表單,并且嵌入到iframe中。

嵌套頁面:(用戶訪問惡意攻擊者主機的頁面,即tab的新頁面)

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>攻擊者主機頁面</title>
    <script type="text/javascript">
    function csrf()
    {
        window.frames['steal'].document.forms[0].submit();
    }
    </script>
</head>
<body onload="csrf()">
<iframe name="steal" display="none" src="./csrf.html">
</iframe>
</body>
</html>

表單頁面csrf.html

<!DOCTYPE html>
<html>
<head>
    <title>csrf</title>
</head>
<body>
<form display="none" action="http://www.game.com/Transfer.php" method="post" >
    <input type="hidden" name="toUserID" value="20">
    <input type="hidden" name="vMoney" value="1000">
</form>
</body>
</html>

    盡管游戲開發者把數據提交和數據接收的升級成了POST,但攻擊者把form表單post請求數據隱藏在了iframe中,客戶端訪問惡意攻擊者的頁面時iframe中的form表單數據請求會被自動觸發,一樣會遭受攻擊。

總結:

  CSRF攻擊是源于Web的隱式身份驗證機制!Web的身份驗證機制雖然可以保證一個請求是來自于某個用戶的瀏覽器,但卻無法保證該請求是用戶批準發送的。


四、CSRF攻擊的防御策略

服務器端防御:

  1、重要數據交互采用POST進行接收,當然是用POST也不是萬能的,偽造一個form表單即可破解

  2、使用驗證碼,只要是涉及到數據交互就先進行驗證碼驗證,這個方法可以完全解決CSRF。但是出于用戶體驗考慮,網站不能給所有的操作都加上驗證碼。因此驗證碼只能作為一種輔助手段,不能作為主要解決方案。

  3、驗證HTTP Referer字段,該字段記錄了此次HTTP請求的來源地址,最常見的應用是圖片防盜鏈。PHP中可以采用APache URL重寫規則進行防御,可參考:http://www.cnblogs.com/phpstudy2015-6/p/6715892.html

  4、為每個表單添加令牌token并驗證

(可以使用cookie或者session進行構造。當然這個token僅僅只是針對CSRF攻擊,在這前提需要解決好XSS攻擊,否則這里也將會是白忙一場【XSS可以偷取客戶端的cookie】) 

  CSRF攻擊之所以能夠成功,是因為攻擊者可以偽造用戶的請求,該請求中所有的用戶驗證信息都存在于Cookie中,因此攻擊者可以在不知道這些驗證信息的情況下直接利用用戶自己的Cookie來通過安全驗證。由此可知,抵御CSRF攻擊的關鍵在于:在請求中放入攻擊者所不能偽造的信息,并且該信息不存在于Cookie之中。

  鑒于此,我們將為每一個表單生成一個隨機數秘鑰,并在服務器端建立一個攔截器來驗證這個token,如果請求中沒有token或者token內容不正確,則認為可能是CSRF攻擊而拒絕該請求。

    由于這個token是隨機不可預測的并且是隱藏看不見的,因此惡意攻擊者就不能夠偽造這個表單進行CSRF攻擊了。

  要求:

  1、要確保同一頁面中每個表單都含有自己唯一的令牌。

  2、每次驗證后需要刪除相應的隨機數。(確保攻擊者即使拿到訪問者的token也無法成功發起攻擊,因為token是一次性的)

token令牌生成示例(僅供參考):

構造令牌類Token.calss.php

<?php
class Token
{
    /**
    * @desc 獲取隨機數
    *
    * @return string 返回隨機數字符串
    */
    private function getTokenValue()
    {
        return md5(uniqid(rand(), true).time());
    }
    
    /**
    * @desc 獲取秘鑰
    *
    * @param $tokenName string | 與秘鑰值配對成鍵值對存入session中(標識符,保證唯一性)
    *
    * @return array 返回存儲在session中秘鑰值
    */
    public function getToken($tokenName)
    {
        $token['name']=$tokenName;      #先將$tokenName放入數組中
        session_start();
        if(@$_SESSION[$tokenName])      #判斷該用戶是否存儲了該session
        {                               #是,則直接返回已經存儲的秘鑰
            $token['value']=$_SESSION[$tokenName];
            return $token;
        }
        else                            #否,則生成秘鑰并保存
        {
            $token['value']=$this->getTokenValue();
            $_SESSION[$tokenName]=$token['value'];
            return $token;
        }
    }

}
#測試
$csrf=new Token();
$name='form1';
$a=$csrf->getToken($name);
echo "<pre>";
print_r($a);
echo "</pre>";
echo "<pre>";
print_r($_SESSION);
echo "</pre>";die;

?>

在form表單中使用token(每次打開form都需要生成生成一次)

<?php
          session_start();
          include(”Token.class.php”);
          $token=new Token();
          $arr=$token->getToken(‘transfer’);    #保證唯一性(標識符)
?>
 <form method=”POST” action=”./transfer.php”>
          <input type=”text” name=”toUserId”>
          <input type=”text” name=”vMoney”>
          <input type="hidden" name="<?php echo $arr['name'] ?>"  value="<?php echo $arr['value']?>" >
          <input type=”submit” name=”submit” value=”Submit”>
 </from>

數據驗證(先校驗token)

<?php
#轉賬表單驗證
session_start();
if($_POST['transfer']==$_SESSION['transfer'])       #先檢驗秘鑰
{
    unset($_SESSION['transfer']);    #刪除已經檢驗的存儲秘鑰
    if ( &&isset($_POST['toUserId'] && isset($_POST['vMoney']))  #驗證
    {
         //相應的轉賬操作
    }
}
else
{
    return false;
}
?>

該方法的思路:

    ①用戶訪問某個表單頁面。

    ②服務端生成一個Token,放在用戶的Session中,或者瀏覽器的Cookie中。【這里已經不考慮XSS攻擊】

    ③在頁面表單附帶上Token參數。

    ④用戶提交請求后, 服務端驗證表單中的Token是否與用戶Session(或Cookies)中的Token一致,一致為合法請求,不是則非法請求。


參考文章:CSRF攻擊與防御

文章版權及轉載聲明:

作者:心月 本文地址:http://www.eojird.tw/websecurity/205.html發布于 2019-07-01
文章轉載或復制請以超鏈接形式并注明出處心月IT博客

分享到:
贊(

發表評論

快捷輸入:

    評論列表 (有 0 條評論,人圍觀)參與討論