HTML form without CSRF protection,HTML表單沒有CSRF保護

HTML form without CSRF protection =HTML表單沒有CSRF保護 
CSRF是僞造客戶端請求的一種攻擊,CSRF的英文全稱是Cross Site Request Forgery,字面上的意思是跨站點僞造請求。這種攻擊方式是國外的安全人員於2000年提出,國內直到06年初才被關注,早期咱們使用過CSRF攻擊實現了DVBBS後臺的SQL注射,同時網上也出現過動易後臺管理員添加的CSRF漏洞等,08年CSRF攻擊方式開始在BLOG、SNS等大型社區類網站的腳本蠕蟲中使用。 
CSRF的定義是強迫受害者的瀏覽器向一個易受攻擊的Web應用程序發送請求,最後達到攻擊者所須要的操做行爲。CSRF漏洞的攻擊通常分爲站內和站外兩種類型: 
CSRF站內類型的漏洞在必定程度上是因爲程序員濫用$_REQUEST類變量形成的,一些敏感的操做原本是要求用戶從表單提交發起POST請求傳參給程序,可是因爲使用了$_REQUEST等變量,程序也接收GET請求傳參,這樣就給攻擊者使用CSRF攻擊創造了條件,通常攻擊者只要把預測好的請求參數放在站內一個貼子或者留言的圖片連接裏,受害者瀏覽了這樣的頁面就會被強迫發起請求。 
CSRF站外類型的漏洞其實就是傳統意義上的外部提交數據問題,通常程序員會考慮給一些留言評論等的表單加上水印以防止SPAM問題,可是爲了用戶的體驗性,一些操做可能沒有作任何限制,因此攻擊者能夠先預測好請求的參數,在站外的Web頁面裏編寫javascript腳本僞造文件請求或和自動提交的表單來實現GET、POST請求,用戶在會話狀態下點擊連接訪問站外的Web頁面,客戶端就被強迫發起請求。 
如何防止此類攻擊: 
部署mdcsoft-ips,mdcsoft waf可完全防護。 

一.CSRF是什麼? 

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

二.CSRF能夠作什麼? 

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

三.CSRF漏洞現狀 

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

四.CSRF的原理 

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


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

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

  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).服務端覈對令牌:
javascript

相關文章
相關標籤/搜索