淺談CSRF攻擊方式

一.CSRF是什麼?javascript

  CSRF(Cross-site request forgery),中文名稱:跨站請求僞造,也被稱爲:one click attack/session riding,縮寫爲:CSRF/XSRF。php

二.CSRF能夠作什麼?html

  你這能夠這麼理解CSRF攻擊:攻擊者盜用了你的身份,以你的名義發送惡意請求。CSRF可以作的事情包括:以你名義發送郵件,發消息,盜取你的帳號,甚至於購買商品,虛擬貨幣轉帳......形成的問題包括:我的隱私泄露以及財產安全。java

三.CSRF漏洞現狀瀏覽器

  CSRF這種攻擊方式在2000年已經被國外的安全人員提出,但在國內,直到06年纔開始被關注,08年,國內外的多個大型社區和交互網站分別 爆出CSRF漏洞,如:NYTimes.com(紐約時報)、Metafilter(一個大型的BLOG網站),YouTube和百度HI......而 如今,互聯網上的許多站點仍對此毫無防備,以致於安全業界稱CSRF爲「沉睡的巨人」。安全

四.CSRF的原理服務器

  下圖簡單闡述了CSRF攻擊的思想:cookie

從上圖能夠看出,要完成一次CSRF攻擊,受害者必須依次完成兩個步驟session

  1.登陸受信任網站A,並在本地生成Cookiedom

  2.在不登出A的狀況下,訪問危險網站B

  看到這裏,你也許會說:「若是我不知足以上兩個條件中的一個,我就不會受到CSRF的攻擊」。是的,確實如此,但你不能保證如下狀況不會發生:

  1.你不能保證你登陸了一個網站後,再也不打開一個tab頁面並訪問另外的網站。

  2.你不能保證你關閉瀏覽器了後,你本地的Cookie馬上過時,你上次的會話已經結束。(事實上,關閉瀏覽器不能結束一個會話,但大多數人都會錯誤的認爲關閉瀏覽器就等於退出登陸/結束會話了......)

  3.上圖中所謂的攻擊網站,多是一個存在其餘漏洞的可信任的常常被人訪問的網站。

 

  上面大概地講了一下CSRF攻擊的思想,下面我將用幾個例子詳細說說具體的CSRF攻擊,這裏我以一個銀行轉帳的操做做爲例子(僅僅是例子,真實的銀行網站沒這麼傻:>)

  示例1:

  銀行網站A,它以GET請求來完成銀行轉帳的操做,如:http://www.mybank.com/Transfer.php?toBankId=11&money=1000

  危險網站B,它裏面有一段HTML的代碼以下:

  < img  src =http://www.mybank.com/Transfer.php?toBankId=11&money=1000 >

  首先,你登陸了銀行網站A,而後訪問危險網站B,噢,這時你會發現你的銀行帳戶少了1000塊......

  爲何會這樣呢?緣由是銀行網站A違反了HTTP規範,使用GET請求更新資源。在訪問危險網站B的以前,你已經登陸了銀行網站A,而B中 的<img>以GET的方式請求第三方資源(這裏的第三方就是指銀行網站了,本來這是一個合法的請求,但這裏被不法分子利用了),因此你的瀏 覽器會帶上你的銀行網站A的Cookie發出Get請求,去獲取資源「http://www.mybank.com /Transfer.php?toBankId=11&money=1000」,結果銀行網站服務器收到請求後,認爲這是一個更新資源操做(轉帳 操做),因此就馬上進行轉帳操做......

  示例2:

  爲了杜絕上面的問題,銀行決定改用POST請求完成轉帳操做。

  銀行網站A的WEB表單以下:  

  < form  action ="Transfer.php"  method ="POST" >
    < p > ToBankId:  < input  type ="text"  name ="toBankId"   /></ p >
    < p > Money:  < input  type ="text"  name ="money"   /></ p >
    < p >< input  type ="submit"  value =" Transfer "   /></ p >
  </ form >

  後臺處理頁面Transfer.php以下:

複製代碼
  <? php
    session_start ();
    if  ( isset ( $_REQUEST [ ' toBankId ' &&   isset ( $_REQUEST [ ' money ' ]))
    {
        buy_stocks(
$_REQUEST [ ' toBankId ' ] ,   $_REQUEST [ ' money ' ]);
    }
  ?>
複製代碼

  危險網站B,仍然只是包含那句HTML代碼:

   < img  src =http://www.mybank.com/Transfer.php?toBankId=11&money=1000 >

  和示例1中的操做同樣,你首先登陸了銀行網站A,而後訪問危險網站B,結果.....和示例1同樣,你再次沒了1000塊~T_T,此次事故的 緣由是:銀行後臺使用了$_REQUEST去獲取請求的數據,而$_REQUEST既能夠獲取GET請求的數據,也能夠獲取POST請求的數據,這就形成 了在後臺處理程序沒法區分這究竟是GET請求的數據仍是POST請求的數據。在PHP中,可使用$_GET和$_POST分別獲取GET請求和POST 請求的數據。在JAVA中,用於獲取請求數據request同樣存在不能區分GET請求數據和POST數據的問題。

  示例3:

  通過前面2個慘痛的教訓,銀行決定把獲取請求數據的方法也改了,改用$_POST,只獲取POST請求的數據,後臺處理頁面Transfer.php代碼以下:

複製代碼
   <? php
    
session_start ();
    
if  ( isset ( $_POST [ ' toBankId ' &&   isset ( $_POST [ ' money ' ]))
    {
        buy_stocks(
$_POST [ ' toBankId ' ] ,   $_POST [ ' money ' ]);
    }
  
?>
複製代碼

  然而,危險網站B與時俱進,它改了一下代碼:

複製代碼
< html >
  < head >
     < script  type ="text/javascript" >
      function  steal()
      {
               iframe 
=  document.frames[ " steal " ];
               iframe.document.Submit(
" transfer " );
      }
    </ script >
  </ head >

  
< body  onload ="steal()" >
     < iframe  name ="steal"  display ="none" >
      < form  method ="POST"  name ="transfer"  action ="http://www.myBank.com/Transfer.php" >
        
< input  type ="hidden"  name ="toBankId"  value ="11" >
        
< input  type ="hidden"  name ="money"  value ="1000" >
      
</ form >
    </ iframe >
  </ body >
</ html >
複製代碼

若是用戶還是繼續上面的操做,很不幸,結果將會是再次不見1000塊......由於這裏危險網站B暗地裏發送了POST請求到銀行!

  總結一下上面3個例子,CSRF主要的攻擊模式基本上是以上的3種,其中以第1,2種最爲嚴重,由於觸發條件很簡單,一 個<img>就能夠了,而第3種比較麻煩,須要使用JavaScript,因此使用的機會會比前面的少不少,但不管是哪一種狀況,只要觸發了 CSRF攻擊,後果都有可能很嚴重。

  理解上面的3種攻擊模式,其實能夠看出,CSRF攻擊是源於WEB的隱式身份驗證機制!WEB的身份驗證機制雖然能夠保證一個請求是來自於某個用戶的瀏覽器,但卻沒法保證該請求是用戶批准發送的

五.CSRF的防護

  我總結了一下看到的資料,CSRF的防護能夠從服務端客戶端兩方面着手,防護效果是從服務端着手效果比較好,如今通常的CSRF防護也都在服務端進行。

  1.服務端進行CSRF防護

  服務端的CSRF方式方法不少樣,但總的思想都是一致的,就是在客戶端頁面增長僞隨機數

  (1).Cookie Hashing(全部表單都包含同一個僞隨機值):

  這多是最簡單的解決方案了,由於攻擊者不能得到第三方的Cookie(理論上),因此表單中的數據也就構造失敗了:>

  <? php
    // 構造加密的Cookie信息
    $value   =  「DefenseSCRF」;
    setcookie (」cookie」 ,   $value ,   time () + 3600 );
  ?>

  在表單裏增長Hash值,以認證這確實是用戶發送的請求。

複製代碼
  <? php
    $hash   =   md5 ( $_COOKIE [ ' cookie ' ]);
  ?>
  < form method = 」POST」 action = 」transfer . php」 >
    < input type = 」text」 name = 」toBankId」 >
    < input type = 」text」 name = 」money」 >
    < input type = 」hidden」 name = 」hash」 value = <?= $hash ; ?> >
    < input type = 」submit」 name = 」submit」 value = 」Submit」 >
  </ form >
複製代碼

  而後在服務器端進行Hash值驗證

複製代碼
       <? php
        if ( isset ( $_POST [ ' check ' ])) {
            
$hash   =   md5 ( $_COOKIE [ ' cookie ' ]);
             if ( $_POST [ ' check ' ==   $hash ) {
                  doJob();
             } 
else  {
        //...

             }
        } 
else  {
      //...

        }
      
?>
複製代碼

  這個方法我的以爲已經能夠杜絕99%的CSRF攻擊了,那還有1%呢....因爲用戶的Cookie很容易因爲網站的XSS漏洞而被盜取,這就 另外的1%。通常的攻擊者看到有須要算Hash值,基本都會放棄了,某些除外,因此若是須要100%的杜絕,這個不是最好的方法。
  (2).驗證碼

  這個方案的思路是:每次的用戶提交都須要用戶在表單中填寫一個圖片上的隨機字符串,厄....這個方案能夠徹底解決CSRF,但我的以爲在易用性方面彷佛不是太好,還有聽聞是驗證碼圖片的使用涉及了一個被稱爲MHTML的Bug,可能在某些版本的微軟IE中受影響。

  (3).One-Time Tokens(不一樣的表單包含一個不一樣的僞隨機值)

  在實現One-Time Tokens時,須要注意一點:就是「並行會話的兼容」。若是用戶在一個站點上同時打開了兩個不一樣的表單,CSRF保護措施不該該影響到他對任何表單的提 交。考慮一下若是每次表單被裝入時站點生成一個僞隨機值來覆蓋之前的僞隨機值將會發生什麼狀況:用戶只能成功地提交他最後打開的表單,由於全部其餘的表單 都含有非法的僞隨機值。必須當心操做以確保CSRF保護措施不會影響選項卡式的瀏覽或者利用多個瀏覽器窗口瀏覽一個站點。

  如下個人實現:

  1).先是令牌生成函數(gen_token()):

複製代碼
      <? php
      function  gen_token() {
    //這裏我是貪方便,實際上單使用Rand()得出的隨機數做爲令牌,也是不安全的。
    //這個能夠參考我寫的Findbugs筆記中的《Random object created and used only once》
           $token   = md5 ( uniqid ( rand () ,   true ));
          
return   $token ;
     }
複製代碼

  2).而後是Session令牌生成函數(gen_stoken()):

複製代碼
      <? php
     
  function  gen_stoken() {
       $pToken = "" ;
      if( $_SESSION [STOKEN_NAME]  == $pToken ){
        //沒有值,賦新值
      
  $_SESSION [STOKEN_NAME]  = gen_token() ;
      }   
      else{
        //繼續使用舊的值
      }

       }
     
?>
複製代碼

  3).WEB表單生成隱藏輸入域的函數:  

複製代碼
     <?php
       function gen_input() {
            gen_stoken();
            echo 「<input type=\」hidden\」 name=\」" . FTOKEN_NAME . 「\」
                 value=\」" . $_SESSION[STOKEN_NAME] . 「\」> 「;
       }
     ?>
複製代碼

  4).WEB表單結構:

複製代碼
     <? php
          
session_start ();
          
include (」functions . php」);
     
?>
     
< form method = 」POST」 action = 」transfer . php」 >
          
< input type = 」text」 name = 」toBankId」 >
          
< input type = 」text」 name = 」money」 >
          
<?  gen_input();  ?>
          
< input type = 」submit」 name = 」submit」 value = 」Submit」 >
     
</ FORM >
複製代碼

  5).服務端覈對令牌:

  這個很簡單,這裏就再也不囉嗦了。

  上面這個其實不徹底符合「並行會話的兼容」的規則,你們能夠在此基礎上修改。

 

  其實還有不少想寫,無奈精力有限,暫且打住,往後補充,若是錯漏,請指出:>

  PS:今天下午寫這篇文檔的時候FF崩潰了一次,寫了一半文章的全沒了,鬱悶很久T_T.......

  轉載請說明出處,謝謝[hyddd(http://www.cnblogs.com/hyddd/)]

相關文章
相關標籤/搜索