Web 安全 之 CSRF

Cross-site request forgery (CSRF)

在本節中,咱們將解釋什麼是跨站請求僞造,並描述一些常見的 CSRF 漏洞示例,同時說明如何防護 CSRF 攻擊。html

什麼是 CSRF

跨站請求僞造(CSRF)是一種 web 安全漏洞,它容許攻擊者誘使用戶執行他們不想執行的操做。攻擊者進行 CSRF 可以部分規避同源策略。web

CSRF 攻擊能形成什麼影響

在成功的 CSRF 攻擊中,攻擊者會使受害用戶無心中執行某個操做。例如,這多是更改他們賬戶上的電子郵件地址、更改密碼或進行資金轉帳。根據操做的性質,攻擊者可能可以徹底控制用戶的賬戶。若是受害用戶在應用程序中具備特權角色,則攻擊者可能可以徹底控制應用程序的全部數據和功能。跨域

CSRF 是如何工做的

要使 CSRF 攻擊成爲可能,必須具有三個關鍵條件:瀏覽器

  • 相關的動做。攻擊者有理由誘使應用程序中發生某種動做。這多是特權操做(例如修改其餘用戶的權限),也多是針對用戶特定數據的任何操做(例如更改用戶本身的密碼)。
  • 基於 Cookie 的會話處理。執行該操做涉及發出一個或多個 HTTP 請求,應用程序僅依賴會話cookie 來標識發出請求的用戶。沒有其餘機制用於跟蹤會話或驗證用戶請求。
  • 沒有不可預測的請求參數。執行該操做的請求不包含攻擊者沒法肯定或猜想其值的任何參數。例如,當致使用戶更改密碼時,若是攻擊者須要知道現有密碼的值,則該功能不會受到攻擊。

假設應用程序包含一個容許用戶更改其郵箱地址的功能。當用戶執行此操做時,會發出以下 HTTP 請求:安全

POST /email/change HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
Cookie: session=yvthwsztyeQkAPzeQ5gHgTvlyxHfsAfE

email=wiener@normal-user.com

這個例子符合 CSRF 要求的條件:服務器

  • 更改用戶賬戶上的郵箱地址的操做會引發攻擊者的興趣。執行此操做後,攻擊者一般可以觸發密碼重置並徹底控制用戶的賬戶。
  • 應用程序使用會話 cookie 來標識發出請求的用戶。沒有其餘標記或機制來跟蹤用戶會話。
  • 攻擊者能夠輕鬆肯定執行操做所需的請求參數的值。

具有這些條件後,攻擊者能夠構建包含如下 HTML 的網頁:cookie

<html>
  <body>
    <form action="https://vulnerable-website.com/email/change" method="POST">
      <input type="hidden" name="email" value="pwned@evil-user.net" />
    </form>
    <script>
      document.forms[0].submit();
    </script>
  </body>
</html>

若是受害用戶訪問了攻擊者的網頁,將發生如下狀況:session

  • 攻擊者的頁面將觸發對易受攻擊的網站的 HTTP 請求。
  • 若是用戶登陸到易受攻擊的網站,其瀏覽器將自動在請求中包含其會話 cookie(假設 SameSite cookies 未被使用)。
  • 易受攻擊的網站將以正常方式處理請求,將其視爲受害者用戶發出的請求,並更改其電子郵件地址。

注意:雖然 CSRF 一般是根據基於 cookie 的會話處理來描述的,但它也出如今應用程序自動向請求添加一些用戶憑據的上下文中,例如 HTTP Basic authentication 基本驗證和 certificate-based authentication 基於證書的身份驗證。併發

如何構造 CSRF 攻擊

手動建立 CSRF 攻擊所需的 HTML 可能很麻煩,尤爲是在所需請求包含大量參數的狀況下,或者在請求中存在其餘異常狀況時。構造 CSRF 攻擊的最簡單方法是使用 Burp Suite Professional(付費軟件) 中的 CSRF PoC generatorapp

如何傳遞 CSRF

跨站請求僞造攻擊的傳遞機制與反射型 XSS 的傳遞機制基本相同。一般,攻擊者會將惡意 HTML 放到他們控制的網站上,而後誘使受害者訪問該網站。這能夠經過電子郵件或社交媒體消息向用戶提供指向網站的連接來實現。或者,若是攻擊被放置在一個流行的網站(例如,在用戶評論中),則只需等待用戶上鉤便可。

請注意,一些簡單的 CSRF 攻擊使用 GET 方法,而且能夠經過易受攻擊網站上的單個 URL 徹底自包含。在這種狀況下,攻擊者可能不須要使用外部站點,而且能夠直接向受害者提供易受攻擊域上的惡意 URL 。在前面的示例中,若是可使用 GET 方法執行更改電子郵件地址的請求,則自包含的攻擊以下所示:

![](https://vulnerable-website.com/email/change?email=pwned@evil-user.net)

防護 CSRF 攻擊

防護 CSRF 攻擊最有效的方法就是在相關請求中使用 CSRF token ,此 token 應該是:

  • 不可預測的,具備高熵的
  • 綁定到用戶的會話中
  • 在相關操做執行前,嚴格驗證每種狀況

可與 CSRF token 一塊兒使用的附加防護措施是 SameSite cookies

常見的 CSRF 漏洞

最有趣的 CSRF 漏洞產生是由於對 CSRF token 的驗證有問題。

在前面的示例中,假設應用程序在更改用戶密碼的請求中須要包含一個 CSRF token

POST /email/change HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 68
Cookie: session=2yQIDcpia41WrATfjPqvm9tOkDvkMvLm

csrf=WfF1szMUHhiokx9AHFply5L2xAOfjRkE&email=wiener@normal-user.com

這看上去好像能夠防護 CSRF 攻擊,由於它打破了 CSRF 須要的必要條件:應用程序再也不僅僅依賴 cookie 進行會話處理,而且請求也包含攻擊者沒法肯定其值的參數。然而,仍然有多種方法能夠破壞防護,這意味着應用程序仍然容易受到 CSRF 的攻擊。

CSRF token 的驗證依賴於請求方法

某些應用程序在請求使用 POST 方法時正確驗證 token ,但在使用 GET 方法時跳過了驗證。

在這種狀況下,攻擊者能夠切換到 GET 方法來繞過驗證併發起 CSRF 攻擊:

GET /email/change?email=pwned@evil-user.net HTTP/1.1
Host: vulnerable-website.com
Cookie: session=2yQIDcpia41WrATfjPqvm9tOkDvkMvLm

CSRF token 的驗證依賴於 token 是否存在

某些應用程序在 token 存在時正確地驗證它,可是若是 token 不存在,則跳過驗證。

在這種狀況下,攻擊者能夠刪除包含 token 的整個參數,從而繞過驗證併發起 CSRF 攻擊:

POST /email/change HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 25
Cookie: session=2yQIDcpia41WrATfjPqvm9tOkDvkMvLm

email=pwned@evil-user.net

CSRF token 未綁定到用戶會話

有些應用程序不驗證 token 是否與發出請求的用戶屬於同一會話。相反,應用程序維護一個已發出的 token 的全局池,並接受該池中出現的任何 token 。

在這種狀況下,攻擊者可使用本身的賬戶登陸到應用程序,獲取有效 token ,而後在 CSRF 攻擊中使用本身的 token 。

CSRF token 被綁定到非會話 cookie

在上述漏洞的變體中,有些應用程序確實將 CSRF token 綁定到了 cookie,但與用於跟蹤會話的同一個 cookie 不綁定。當應用程序使用兩個不一樣的框架時,很容易發生這種狀況,一個用於會話處理,另外一個用於 CSRF 保護,這兩個框架沒有集成在一塊兒:

POST /email/change HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 68
Cookie: session=pSJYSScWKpmC60LpFOAHKixuFuM4uXWF; csrfKey=rZHCnSzEp8dbI6atzagGoSYyqJqTz5dv

csrf=RhV7yQDO0xcq9gLEah2WVbmuFqyOq7tY&email=wiener@normal-user.com

這種狀況很難利用,但仍然存在漏洞。若是網站包含任何容許攻擊者在受害者瀏覽器中設置 cookie 的行爲,則可能發生攻擊。攻擊者可使用本身的賬戶登陸到應用程序,獲取有效的 token 和關聯的 cookie ,利用 cookie 設置行爲將其 cookie 放入受害者的瀏覽器中,並在 CSRF 攻擊中向受害者提供 token 。

注意:cookie 設置行爲甚至沒必要與 CSRF 漏洞存在於同一 Web 應用程序中。若是所控制的 cookie 具備適當的範圍,則能夠利用同一整體 DNS 域中的任何其餘應用程序在目標應用程序中設置 cookie 。例如,staging.demo.normal-website.com 域上的 cookie 設置函數能夠放置提交到 secure.normal-website.com 上的 cookie 。

CSRF token 僅要求與 cookie 中的相同

在上述漏洞的進一步變體中,一些應用程序不維護已發出 token 的任何服務端記錄,而是在 cookie 和請求參數中複製每一個 token 。在驗證後續請求時,應用程序只需驗證在請求參數中提交的 token 是否與在 cookie 中提交的值匹配。這有時被稱爲針對 CSRF 的「雙重提交」防護,之因此被提倡,是由於它易於實現,而且避免了對任何服務端狀態的須要:

POST /email/change HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 68
Cookie: session=1DQGdzYbOJQzLP7460tfyiv3do7MjyPw; csrf=R8ov2YBfTYmzFyjit8o2hKBuoIjXXVpa

csrf=R8ov2YBfTYmzFyjit8o2hKBuoIjXXVpa&email=wiener@normal-user.com

在這種狀況下,若是網站包含任何 cookie 設置功能,攻擊者能夠再次執行 CSRF 攻擊。在這裏,攻擊者不須要得到本身的有效 token 。他們只需發明一個 token ,利用 cookie 設置行爲將 cookie 放入受害者的瀏覽器中,並在 CSRF 攻擊中向受害者提供此 token 。

基於 Referer 的 CSRF 防護

除了使用 CSRF token 進行防護以外,有些應用程序使用 HTTP Referer 頭去防護 CSRF 攻擊,一般是驗證請求來自應用程序本身的域名。這種方法一般不太有效,並且常常會被繞過。

注意:HTTP Referer 頭是一個可選的請求頭,它包含連接到所請求資源的網頁的 URL 。一般,當用戶觸發 HTTP 請求時,好比單擊連接或提交表單,瀏覽器會自動添加它。然而存在各類方法,容許連接頁面保留或修改 Referer 頭的值。這一般是出於隱私考慮。

Referer 的驗證依賴於其是否存在

某些應用程序當請求中有 Referer 頭時會驗證它,可是若是沒有的話,則跳過驗證。

在這種狀況下,攻擊者能夠精心設計其 CSRF 攻擊,使受害用戶的瀏覽器在請求中丟棄 Referer 頭。實現這一點有多種方法,但最簡單的是在託管 CSRF 攻擊的 HTML 頁面中使用 META 標記:

<meta name="referrer" content="never">

Referer 的驗證能夠被規避

某些應用程序以一種能夠被繞過的方式驗證 Referer 頭。例如,若是應用程序只是驗證 Referer 是否包含本身的域名,那麼攻擊者能夠將所需的值放在 URL 的其餘位置:

http://attacker-website.com/csrf-attack?vulnerable-website.com

若是應用程序驗證 Referer 中的域以預期值開頭,那麼攻擊者能夠將其做爲本身域的子域:

http://vulnerable-website.com.attacker-website.com/csrf-attack

CSRF tokens

在本節中,咱們將解釋什麼是 CSRF token,它們是如何防護的 CSRF 攻擊,以及如何生成和驗證CSRF token 。

什麼是 CSRF token

CSRF token 是一個惟一的、祕密的、不可預測的值,它由服務端應用程序生成,並以這種方式傳輸到客戶端,使得它包含在客戶端發出的後續 HTTP 請求中。當發出後續請求時,服務端應用程序將驗證請求是否包含預期的 token ,並在 token 丟失或無效時拒絕該請求。

因爲攻擊者沒法肯定或預測用戶的 CSRF token 的值,所以他們沒法構造出一個應用程序驗證所需所有參數的請求。因此 CSRF token 能夠防止 CSRF 攻擊。

CSRF token 應該如何生成

CSRF token 應該包含顯著的熵,而且具備很強的不可預測性,其一般與會話令牌具備相同的特性。

您應該使用加密強度僞隨機數生成器(PRNG),該生成器附帶建立時的時間戳以及靜態密碼。

若是您須要 PRNG 強度以外的進一步保證,能夠經過將其輸出與某些特定於用戶的熵鏈接來生成單獨的令牌,並對整個結構進行強哈希。這給試圖分析令牌的攻擊者帶來了額外的障礙。

如何傳輸 CSRF token

CSRF token 應被視爲機密,並在其整個生命週期中以安全的方式進行處理。一種一般有效的方法是將令牌傳輸到使用 POST 方法提交的 HTML 表單的隱藏字段中的客戶端。提交表單時,令牌將做爲請求參數包含:

<input type="hidden" name="csrf-token" value="CIwNZNlR4XbisJF39I8yWnWX9wX4WFoz" />

爲了安全起見,包含 CSRF token 的字段應該儘早放置在 HTML 文檔中,最好是在任何非隱藏的輸入字段以前,以及在 HTML 中嵌入用戶可控制數據的任何位置以前。這能夠對抗攻擊者使用精心編制的數據操縱 HTML 文檔並捕獲其部份內容的各類技術。

另外一種方法是將令牌放入 URL query 字符串中,這種方法的安全性稍差,由於 query 字符串:

  • 記錄在客戶端和服務器端的各個位置;
  • 容易在 HTTP Referer 頭中傳輸給第三方;
  • 能夠在用戶的瀏覽器中顯示在屏幕上。

某些應用程序在自定義請求頭中傳輸 CSRF token 。這進一步防止了攻擊者預測或捕獲另外一個用戶的令牌,由於瀏覽器一般不容許跨域發送自定義頭。然而,這種方法將應用程序限制爲使用 XHR 發出受 CSRF 保護的請求(與 HTML 表單相反),而且在許多狀況下可能被認爲過於複雜。

CSRF token 不該在 cookie 中傳輸。

如何驗證 CSRF token

當生成 CSRF token 時,它應該存儲在服務器端的用戶會話數據中。當接收到須要驗證的後續請求時,服務器端應用程序應驗證該請求是否包含與存儲在用戶會話中的值相匹配的令牌。不管請求的HTTP 方法或內容類型如何,都必須執行此驗證。若是請求根本不包含任何令牌,則應以與存在無效令牌時相同的方式拒絕請求。


XSS vs CSRF

在本節中,咱們將解釋 XSSCSRF 之間的區別,並討論 CSRF token 是否有助於防護 XSS 攻擊。

XSS 和 CSRF 之間有啥區別

跨站腳本攻擊 XSS 容許攻擊者在受害者用戶的瀏覽器中執行任意 JavaScript 。

跨站請求僞造 CSRF 容許攻擊者僞造受害用戶執行他們不打算執行的操做。

XSS 漏洞的後果一般比 CSRF 漏洞更嚴重:

  • CSRF 一般只適用於用戶可以執行的操做的子集。一般,許多應用程序都實現 CSRF 防護,可是忽略了暴露的一兩個操做。相反,成功的 XSS 攻擊一般能夠執行用戶可以執行的任何操做,而無論該漏洞是在什麼功能中產生的。
  • CSRF 能夠被描述爲一個「單向」漏洞,由於儘管攻擊者能夠誘導受害者發出 HTTP 請求,但他們沒法從該請求中檢索響應。相反,XSS 是「雙向」的,由於攻擊者注入的腳本能夠發出任意請求、讀取響應並將數據傳輸到攻擊者選擇的外部域。

CSRF token 可否防護 XSS 攻擊

一些 XSS 攻擊確實能夠經過有效使用 CSRF token 來進行防護。假設有一個簡單的反射型 XSS 漏洞,其能夠被利用以下:

https://insecure-website.com/status?message=<script>/*+Bad+stuff+here...+*/</script>

如今,假設漏洞函數包含一個 CSRF token :

https://insecure-website.com/status?csrf-token=CIwNZNlR4XbisJF39I8yWnWX9wX4WFoz&message=<script>/*+Bad+stuff+here...+*/</script>

若是服務器正確地驗證了 CSRF token ,並拒絕了沒有有效令牌的請求,那麼該令牌確實能夠防止此 XSS 漏洞的利用。這裏的關鍵點是「跨站腳本」的攻擊中涉及到了跨站請求,所以經過防止攻擊者僞造跨站請求,該應用程序可防止對 XSS 漏洞的輕度攻擊。

這裏有一些重要的注意事項:

  • 若是反射型 XSS 漏洞存在於站點上任何其餘不受 CSRF token 保護的函數內,則能夠以常規方式利用該 XSS 漏洞。
  • 若是站點上的任何地方都存在可利用的 XSS 漏洞,則能夠利用該漏洞使受害用戶執行操做,即便這些操做自己受到 CSRF token 的保護。在這種狀況下,攻擊者的腳本能夠請求相關頁面獲取有效的 CSRF token,而後使用該令牌執行受保護的操做。
  • CSRF token 不保護存儲型 XSS 漏洞。若是受 CSRF token 保護的頁面也是存儲型 XSS 漏洞的輸出點,則能夠以一般的方式利用該 XSS 漏洞,而且當用戶訪問該頁面時,將執行 XSS 有效負載。

SameSite cookies

某些網站使用 SameSite cookies 防護 CSRF 攻擊。

這個 SameSite 屬性可用於控制是否以及如何在跨站請求中提交 cookie 。經過設置會話 cookie 的屬性,應用程序能夠防止瀏覽器默認自動向請求添加 cookie 的行爲,而無論cookie 來自何處。

這個 SameSite 屬性在服務器的 Set-Cookie 響應頭中設置,該屬性能夠設爲 Strict 嚴格或者 Lax 鬆懈。例如:

SetCookie: SessionId=sYMnfCUrAlmqVVZn9dqevxyFpKZt30NN; SameSite=Strict;

SetCookie: SessionId=sYMnfCUrAlmqVVZn9dqevxyFpKZt30NN; SameSite=Lax;

若是 SameSite 屬性設置爲 Strict ,則瀏覽器將不會在來自其餘站點的任何請求中包含cookie。這是最具防護性的選擇,但它可能會損害用戶體驗,由於若是登陸的用戶經過第三方連接訪問某個站點,那麼他們將不會登陸,而且須要從新登陸,而後才能以正常方式與站點交互。

若是 SameSite 屬性設置爲 Lax ,則瀏覽器將在來自另外一個站點的請求中包含cookie,但前提是知足如下兩個條件:

  • 請求使用 GET 方法。使用其餘方法(如 POST )的請求將不會包括 cookie 。
  • 請求是由用戶的頂級導航(如單擊連接)產生的。其餘請求(如由腳本啓動的請求)將不會包括 cookie 。

使用 SameSiteLax 模式確實對 CSRF 攻擊提供了部分防護,由於 CSRF 攻擊的目標用戶操做一般使用 POST 方法實現。這裏有兩個重要的注意事項:

  • 有些應用程序確實使用 GET 請求實現敏感操做。
  • 許多應用程序和框架可以容忍不一樣的 HTTP 方法。在這種狀況下,即便應用程序自己設計使用的是 POST 方法,但它實際上也會接受被切換爲使用 GET 方法的請求。

出於上述緣由,不建議僅依賴 SameSite Cookie 來抵禦 CSRF 攻擊。當其與 CSRF token 結合使用時,SameSite cookies 能夠提供額外的防護層,並減輕基於令牌的防護中的任何缺陷。

image

相關文章
相關標籤/搜索