【網絡安全】CSRF攻擊詳解

什麼是CSRF攻擊

CSRF(Cross-Site Request Forgery)的全稱是「跨站請求僞造」,也被稱爲「One Click Attack」或者「Session Riding」,一般縮寫爲CSRF或者XSRF。CSRF的中文名稱儘管聽起來像跨站腳本攻擊(XSS),但它與XSS很是不一樣,而且攻擊方式幾乎相左。XSS利用站點內的信任用戶,而CSRF則經過假裝來自受信任用戶的請求來攻擊受信任的網站。與XSS攻擊相比,CSRF攻擊每每不大流行(所以對其進行防範的資源也至關稀少)和難以防範,因此被認爲比XSS更具危險性。php

咱們能夠這麼理解CSRF攻擊:攻擊者首先盜用了你的身份,而後以你的名義進行某些非法操做。CSRF可以使用你的帳戶發送郵件,獲取你的敏感信息,甚至盜走你的帳戶購買商品等。CSRF攻擊實際上是利用了web中用戶身份認證驗證的一個漏洞:簡單的身份驗證僅僅能保證請求發自某個用戶的瀏覽器,卻不能保證請求自己是用戶自願發出的。html

CSRF攻擊的流程

攻擊流程

CSRF攻擊攻擊原理及過程以下:java

  1. 用戶C打開瀏覽器,訪問受信任網站A,輸入用戶名和密碼請求登陸網站A;
  2. 在用戶信息經過驗證後,網站A產生Cookie信息並返回給瀏覽器,此時用戶登陸網站A成功,能夠正常發送請求到網站A;
  3. 用戶未退出網站A以前,在同一瀏覽器中,打開一個TAB頁訪問網站B;
  4. 網站B接收到用戶請求後,返回一些攻擊性代碼,併發出一個請求要求訪問第三方站點A;
  5. 瀏覽器在接收到這些攻擊性代碼後,根據網站B的請求,在用戶不知情的狀況下攜帶Cookie信息,向網站A發出請求。網站A並不知道該請求實際上是由B發起的,因此會根據用戶C的Cookie信息以C的權限處理該請求,致使來自網站B的惡意代碼被執行。

從上面的流程能夠看出,想要達成CSRF攻擊,必須達到兩個基本條件:程序員

  • 登陸受信任站點A,並在本地生成Cookie。
  • 在不登出A的狀況下,訪問危急站點B。

常見的CSRF攻擊類型

  1. GET類型的CSRF
    僅僅需要一個HTTP請求。就可以構造一次簡單的CSRF。 
    樣例: 
銀行站點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中的 一個合法的請求,但這裏被不法分子利用了)。因此你的瀏覽器會帶上你的銀行站點A的Cookie發出Get請求,去獲取資源以GET的方式請求第三方資源(這裏的第三方就是指銀行站點了,本來這是http://www.mybank.com/Transfer.php?toBankId=11&money=1000 ,結果銀行站點服務器收到請求後,以爲這是一個更新資源操做(轉帳操做),因此就立馬進行轉帳操做。web

  1. POST類型的CSRF
    這種類型的CSRF危害沒有GET型的大,利用起來一般使用的是一個自動提交的表單,如:
<form action=http://wooyun.org/csrf.php method=POST>
    <input type="text" name="xx" value="11" />
</form>
<script> document.forms[0].submit(); </script>

訪問該頁面後,表單會自動提交,至關於模擬用戶完成了一次POST操做。瀏覽器

CSRF漏洞測試

檢測CSRF漏洞是一項比較繁瑣的工做,最簡單的方法就是抓取一個正常請求的數據包,去掉Referer字段後再從新提交,若是該提交還有效,那麼基本上能夠肯定存在CSRF漏洞。安全

隨着對CSRF漏洞研究的不斷深刻,不斷涌現出一些專門針對CSRF漏洞進行檢測的工具,如CSRFTester,CSRF Request Builder等。以CSRFTester工具爲例,CSRF漏洞檢測工具的測試原理以下:使用CSRFTester進行測試時,首先須要抓取咱們在瀏覽器中訪問過的全部連接以及全部的表單等信息,而後經過在CSRFTester中修改相應的表單等信息,從新提交,這至關於一次僞造客戶端請求。若是修改後的測試請求成功被網站服務器接受,則說明存在CSRF漏洞,固然此款工具也能夠被用來進行CSRF攻擊。服務器

預防CSRF攻擊

1. 驗證HTTP Referer字段
根據 HTTP 協議,在 HTTP 頭中有一個字段叫 Referer,它記錄了該 HTTP 請求的來源地址。在一般狀況下,訪問一個安全受限頁面的請求來自於同一個網站,好比須要訪問 http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory,用戶必須先登錄 bank.example,而後經過點擊頁面上的按鈕來觸發轉帳事件。這時,該轉賬請求的 Referer 值就會是轉帳按鈕所在的頁面的 URL,一般是以 bank.example 域名開頭的地址。而若是黑客要對銀行網站實施 CSRF 攻擊,他只能在他本身的網站構造請求,當用戶經過黑客的網站發送請求到銀行時,該請求的 Referer 是指向黑客本身的網站。所以,要防護 CSRF 攻擊,銀行網站只須要對於每個轉帳請求驗證其 Referer 值,若是是以 bank.example 開頭的域名,則說明該請求是來自銀行網站本身的請求,是合法的。若是 Referer 是其餘網站的話,則有多是黑客的 CSRF 攻擊,拒絕該請求。cookie

這種方法的顯而易見的好處就是簡單易行,網站的普通開發人員不須要操心 CSRF 的漏洞,只須要在最後給全部安全敏感的請求統一增長一個攔截器來檢查 Referer 的值就能夠。特別是對於當前現有的系統,不須要改變當前系統的任何已有代碼和邏輯,沒有風險,很是便捷。

然而,這種方法並不是萬無一失。Referer 的值是由瀏覽器提供的,雖然 HTTP 協議上有明確的要求,可是每一個瀏覽器對於 Referer 的具體實現可能有差異,並不能保證瀏覽器自身沒有安全漏洞。使用驗證 Referer 值的方法,就是把安全性都依賴於第三方(即瀏覽器)來保障,從理論上來說,這樣並不安全。事實上,對於某些瀏覽器,好比 IE6 或 FF2,目前已經有一些方法能夠篡改 Referer 值。若是 bank.example 網站支持 IE6 瀏覽器,黑客徹底能夠把用戶瀏覽器的 Referer 值設爲以 bank.example 域名開頭的地址,這樣就能夠經過驗證,從而進行 CSRF 攻擊。

即使是使用最新的瀏覽器,黑客沒法篡改 Referer 值,這種方法仍然有問題。由於 Referer 值會記錄下用戶的訪問來源,有些用戶認爲這樣會侵犯到他們本身的隱私權,特別是有些組織擔憂 Referer 值會把組織內網中的某些信息泄露到外網中。所以,用戶本身能夠設置瀏覽器使其在發送請求時再也不提供 Referer。當他們正常訪問銀行網站時,網站會由於請求沒有 Referer 值而認爲是 CSRF 攻擊,拒絕合法用戶的訪問。

驗證Referer方式總結

  • 優勢:使用方便,開發簡單,必定程度上能預防CSRF攻擊;
  • 缺點:這種機制徹底依託於瀏覽器,Referer字段容易被故意篡改,或者被禁用。

2. 請求中添加token並驗證
token就是服務端返回給客戶端相似sessionid那樣一長串的類值(長是爲了防暴力猜解)。csrf依賴於瀏覽器該問連接時自動對應網站的cookie帶上,token不放cookie(通常form表單加個hidden屬性的input標籤來存放)csrf就無法獲取token,這樣咱們就能夠經過檢測發送過來的數據包中是否有正確的token值來決定是否響應請求。

在講清token防護的原理後,咱們再來說token的設計,由於token方式給人的感受很複雜使人望而生畏。

咱們首先明確一個問題,就是可以防止csrf攻擊的token,並不須要每次請求都不同,在用戶登陸後到退出前的這整個過程當中的全部請求token徹底能夠是同樣。由於(在基於沒有其餘漏洞會泄漏本次會話的token的設想下)黑客是沒法獲取用戶的tokne,因此又何須每一個請求都要生成一個新的token呢。(token每次請求都要不同的想法是受防重放攻擊的影響)只考濾防csrf不考濾防重放的狀況下,token設計就簡單多了。

使用sessionid做爲token設計:在csrf中cookie是瀏覽器本身帶上的,本質而言用戶的sessionid並未丟失(也就是攻擊者並不能知道sessionid是多少),基於此咱們徹底能夠不用另傳一個值只需直接將sessionid做爲token便可(或者也能夠作些運算好比取sessionid的某些值作個md5來作爲token,意思都差很少)。判斷代碼相似 if session["id"] == $_POST["token"]

與sessionid同時返回的token設計:在生成sessionid的同時生成一個token(服務端token能夠存於session變量中)返回給客戶端,客戶端保存該token每次請求時都在form表單中提交該值。判斷代碼相似if session["token"] == $_POST["token"]

這種方法要比檢查 Referer 要安全一些,token 能夠在用戶登錄後產生並放於 session 之中,而後在每次請求時把 token 從 session 中拿出,與請求中的 token 進行比對,但這種方法的難點在於如何把 token 以參數的形式加入請求。對於 GET 請求,token 將附在請求地址以後,這樣 URL 就變成 http://url?csrftoken=tokenvalue。 而對於 POST 請求來講,要在 form 的最後加上 ,這樣就把 token 以參數的形式加入請求了。可是,在一個網站中,能夠接受請求的地方很是多,要對於每個請求都加上 token 是很麻煩的,而且很容易漏掉,一般使用的方法就是在每次頁面加載時,使用 javascript 遍歷整個 dom 樹,對於 dom 中全部的 a 和 form 標籤後加入 token。這樣能夠解決大部分的請求,可是對於在頁面加載以後動態生成的 html 代碼,這種方法就沒有做用,還須要程序員在編碼時手動添加 token。另外還有一個問題就是怎麼保障token自己的存儲安全,不要被黑客截獲。

驗證token方式總結

  • 安全程度比Referer的方式要高;
  • 實現方式上稍微複雜;
  • 須要保證token存儲的安全性。

3. 在 HTTP 頭中自定義屬性並驗證
這種方法也是使用 token 並進行驗證,和上一種方法不一樣的是,這裏並非把 token 以參數的形式置於 HTTP 請求之中,而是把它放到 HTTP 頭中自定義的屬性裏。經過 XMLHttpRequest 這個類,能夠一次性給全部該類請求加上 csrftoken 這個 HTTP 頭屬性,並把 token 值放入其中。這樣解決了上種方法在請求中加入 token 的不便,同時,經過 XMLHttpRequest 請求的地址不會被記錄到瀏覽器的地址欄,也不用擔憂 token 會透過 Referer 泄露到其餘網站中去。

然而這種方法的侷限性很是大。XMLHttpRequest 請求一般用於 Ajax 方法中對於頁面局部的異步刷新,並不是全部的請求都適合用這個類來發起,並且經過該類請求獲得的頁面不能被瀏覽器所記錄下,從而進行前進,後退,刷新,收藏等操做,給用戶帶來不便。另外,對於沒有進行 CSRF防禦的遺留系統來講,要採用這種方法來進行防禦,要把全部請求都改成 XMLHttpRequest 請求,這樣幾乎是要重寫整個網站,這代價無疑是不能接受的。

驗證Head屬性方式總結

  • 使用方式較簡單,並且token不容易泄露
  • 使用場合較少,侷限性較大。

參考

相關文章
相關標籤/搜索