跨站請求偽造保護

CSRF 中介層和樣板標籤提供易於使用的保護,防止跨站請求偽造。當惡意網站包含一個連結、一個表單按鈕或一些 JavaScript,目的是使用在其瀏覽器中訪問惡意網站的已登入使用者的憑證,在您的網站上執行某些操作時,就會發生這種類型的攻擊。相關類型的攻擊「登入 CSRF」,其中攻擊網站欺騙使用者的瀏覽器使用其他人的憑證登入網站,也在此涵蓋範圍內。

防止 CSRF 攻擊的第一道防線是確保 GET 請求(以及由RFC 9110 第 9.2.1 節定義的其他「安全」方法)不會產生副作用。接著,可以使用如何使用 Django 的 CSRF 保護中概述的步驟,保護透過「不安全」方法(例如 POST、PUT 和 DELETE)進行的請求。

運作方式

CSRF 保護基於以下幾點:

  1. 一個 CSRF Cookie,它是一個隨機的秘密值,其他網站無法存取。

    CsrfViewMiddleware 會在每次呼叫 django.middleware.csrf.get_token() 時,將此 Cookie 與回應一起傳送。它也可以在其他情況下傳送。基於安全原因,每次使用者登入時,秘密的值都會變更。

  2. 一個名為「csrfmiddlewaretoken」的隱藏表單欄位,存在於所有外送的 POST 表單中。

    為了防止 BREACH 攻擊,此欄位的值不只是秘密。它使用遮罩在每次回應中以不同的方式加擾。遮罩會在每次呼叫 get_token() 時隨機產生,因此表單欄位的值每次都會不同。

    這個部分由 csrf_token 樣板標籤完成。

  3. 對於所有未使用 HTTP GET、HEAD、OPTIONS 或 TRACE 的傳入請求,必須存在 CSRF Cookie,且必須存在且正確的「csrfmiddlewaretoken」欄位。如果沒有,使用者會收到 403 錯誤。

    在驗證「csrfmiddlewaretoken」欄位值時,只會將秘密(而不是完整權杖)與 Cookie 值中的秘密進行比較。這允許使用不斷變化的權杖。雖然每個請求可以使用自己的權杖,但秘密對所有人來說都是通用的。

    此檢查由 CsrfViewMiddleware 完成。

  4. CsrfViewMiddleware 會根據目前主機和 CSRF_TRUSTED_ORIGINS 設定,驗證瀏覽器提供的Origin 標頭。這提供了針對跨子網域攻擊的保護。

  5. 此外,對於 HTTPS 請求,如果未提供 Origin 標頭,CsrfViewMiddleware 會執行嚴格的 referer 檢查。這表示即使子網域可以在您的網域上設定或修改 Cookie,它也不能強制使用者張貼到您的應用程式,因為該請求不會來自您自己的確切網域。

    這也解決了在使用與工作階段無關的秘密時,在 HTTPS 下可能發生的中間人攻擊,因為 HTTP Set-Cookie 標頭即使在與 HTTPS 下的網站交談時,也會被用戶端接受(很不巧)。(referer 檢查不會對 HTTP 請求執行,因為在 HTTP 下,Referer 標頭的存在不夠可靠。)

    如果已設定 CSRF_COOKIE_DOMAIN 設定,則會將 referer 與其比較。您可以透過包含開頭點來允許跨子網域請求。例如,CSRF_COOKIE_DOMAIN = '.example.com' 將允許來自 www.example.comapi.example.com 的 POST 請求。如果未設定該設定,則 referer 必須與 HTTP Host 標頭相符。

    可以使用 CSRF_TRUSTED_ORIGINS 設定,將接受的 referer 擴展到目前主機或 Cookie 網域之外。

這確保只有來自受信任網域的表單才能用於將資料張貼回。

它故意忽略 GET 請求(以及由RFC 9110 第 9.2.1 節定義為「安全」的其他請求)。這些請求永遠不應該有任何潛在危險的副作用,因此使用 GET 請求的 CSRF 攻擊應該是無害的。RFC 9110 第 9.2.1 節將 POST、PUT 和 DELETE 定義為「不安全」,為了獲得最大程度的保護,所有其他方法也被假定為不安全。

CSRF 保護無法防止中間人攻擊,因此請將HTTPSHTTP Strict Transport Security一起使用。它也假設HOST 標頭的驗證,並且您的網站上沒有任何跨網站指令碼漏洞(因為 XSS 漏洞已經允許攻擊者執行 CSRF 漏洞允許的任何操作,以及更糟的情況)。

移除 Referer 標頭

為了避免向第三方網站洩露 referer URL,您可能想要在網站的 <a> 標籤上停用 referer。例如,您可能會使用 <meta name="referrer" content="no-referrer"> 標籤,或包含 Referrer-Policy: no-referrer 標頭。由於 CSRF 保護對 HTTPS 請求執行嚴格的 referer 檢查,這些技術會導致使用「不安全」方法的請求發生 CSRF 失敗。相反地,對於指向第三方網站的連結,請使用替代方案,例如 <a rel="noreferrer" ...>"

限制

網站內的子網域將能夠為整個網域在用戶端上設定 Cookie。透過設定 Cookie 並使用對應的權杖,子網域將能夠規避 CSRF 保護。避免這種情況的唯一方法是確保子網域由受信任的使用者控制(或至少無法設定 Cookie)。請注意,即使沒有 CSRF,也存在其他漏洞(例如工作階段固定),這使得將子網域給予不受信任的第三方是一個壞主意,而且這些漏洞無法使用目前的瀏覽器輕鬆修復。

公用程式

以下範例假設您正在使用基於函式的檢視。如果您正在使用基於類別的檢視,您可以參考裝飾基於類別的檢視

csrf_exempt(view)[原始碼]

此裝飾器會將檢視標記為免除中介層確保的保護。範例

from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt


@csrf_exempt
def my_view(request):
    return HttpResponse("Hello world")
在 Django 5.0 中變更

新增了對包裝非同步檢視函式的支援。

csrf_protect(view)

CsrfViewMiddleware 的保護提供給檢視的裝飾器。

用法

from django.shortcuts import render
from django.views.decorators.csrf import csrf_protect


@csrf_protect
def my_view(request):
    c = {}
    # ...
    return render(request, "a_template.html", c)
在 Django 5.0 中變更

新增了對包裝非同步檢視函式的支援。

requires_csrf_token(view)

通常,如果 CsrfViewMiddleware.process_view 或類似 csrf_protect 的等效項目未執行,則 csrf_token 樣板標籤將無法運作。檢視裝飾器 requires_csrf_token 可用於確保樣板標籤可以運作。此裝飾器的運作方式與 csrf_protect 類似,但永遠不會拒絕傳入的請求。

範例

from django.shortcuts import render
from django.views.decorators.csrf import requires_csrf_token


@requires_csrf_token
def my_view(request):
    c = {}
    # ...
    return render(request, "a_template.html", c)
在 Django 5.0 中變更

新增了對包裝非同步檢視函式的支援。

這個裝飾器會強制視圖傳送 CSRF Cookie。

在 Django 5.0 中變更

新增了對包裝非同步檢視函式的支援。

設定

可以使用許多設定來控制 Django 的 CSRF 行為

常見問題

發布任意的 CSRF 令牌配對(cookie 和 POST 資料)是否為漏洞?

不,這是設計使然。在沒有中間人攻擊的情況下,攻擊者無法將 CSRF 令牌 cookie 發送到受害者的瀏覽器,因此成功的攻擊需要透過 XSS 或類似方式獲取受害者瀏覽器的 cookie,在這種情況下,攻擊者通常不需要 CSRF 攻擊。

某些安全稽核工具將此標記為問題,但如前所述,攻擊者無法竊取使用者瀏覽器的 CSRF cookie。使用 Firebug、Chrome 開發人員工具等「竊取」或修改您自己的令牌並非漏洞。

Django 的 CSRF 保護預設未連結到 Session 是否為問題?

不,這是設計使然。不將 CSRF 保護連結到 Session 允許在諸如pastebin之類的網站上使用該保護,這些網站允許來自沒有 Session 的匿名使用者的提交。

如果您希望將 CSRF 令牌儲存在使用者的 Session 中,請使用 CSRF_USE_SESSIONS 設定。

為什麼使用者在登入後可能會遇到 CSRF 驗證失敗?

出於安全原因,每次使用者登入時都會輪換 CSRF 令牌。任何在登入前產生的表單頁面都將具有舊的、無效的 CSRF 令牌,需要重新載入。如果使用者在登入後使用返回按鈕或在不同的瀏覽器分頁中登入,可能會發生這種情況。

返回頂部