跨站請求偽造保護¶
CSRF 中介層和樣板標籤提供易於使用的保護,防止跨站請求偽造。當惡意網站包含一個連結、一個表單按鈕或一些 JavaScript,目的是使用在其瀏覽器中訪問惡意網站的已登入使用者的憑證,在您的網站上執行某些操作時,就會發生這種類型的攻擊。相關類型的攻擊「登入 CSRF」,其中攻擊網站欺騙使用者的瀏覽器使用其他人的憑證登入網站,也在此涵蓋範圍內。
防止 CSRF 攻擊的第一道防線是確保 GET 請求(以及由RFC 9110 第 9.2.1 節定義的其他「安全」方法)不會產生副作用。接著,可以使用如何使用 Django 的 CSRF 保護中概述的步驟,保護透過「不安全」方法(例如 POST、PUT 和 DELETE)進行的請求。
運作方式¶
CSRF 保護基於以下幾點:
一個 CSRF Cookie,它是一個隨機的秘密值,其他網站無法存取。
CsrfViewMiddleware
會在每次呼叫django.middleware.csrf.get_token()
時,將此 Cookie 與回應一起傳送。它也可以在其他情況下傳送。基於安全原因,每次使用者登入時,秘密的值都會變更。一個名為「csrfmiddlewaretoken」的隱藏表單欄位,存在於所有外送的 POST 表單中。
為了防止 BREACH 攻擊,此欄位的值不只是秘密。它使用遮罩在每次回應中以不同的方式加擾。遮罩會在每次呼叫
get_token()
時隨機產生,因此表單欄位的值每次都會不同。這個部分由
csrf_token
樣板標籤完成。對於所有未使用 HTTP GET、HEAD、OPTIONS 或 TRACE 的傳入請求,必須存在 CSRF Cookie,且必須存在且正確的「csrfmiddlewaretoken」欄位。如果沒有,使用者會收到 403 錯誤。
在驗證「csrfmiddlewaretoken」欄位值時,只會將秘密(而不是完整權杖)與 Cookie 值中的秘密進行比較。這允許使用不斷變化的權杖。雖然每個請求可以使用自己的權杖,但秘密對所有人來說都是通用的。
此檢查由
CsrfViewMiddleware
完成。CsrfViewMiddleware
會根據目前主機和CSRF_TRUSTED_ORIGINS
設定,驗證瀏覽器提供的Origin 標頭。這提供了針對跨子網域攻擊的保護。此外,對於 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.com
和api.example.com
的 POST 請求。如果未設定該設定,則 referer 必須與 HTTPHost
標頭相符。可以使用
CSRF_TRUSTED_ORIGINS
設定,將接受的 referer 擴展到目前主機或 Cookie 網域之外。
這確保只有來自受信任網域的表單才能用於將資料張貼回。
它故意忽略 GET 請求(以及由RFC 9110 第 9.2.1 節定義為「安全」的其他請求)。這些請求永遠不應該有任何潛在危險的副作用,因此使用 GET 請求的 CSRF 攻擊應該是無害的。RFC 9110 第 9.2.1 節將 POST、PUT 和 DELETE 定義為「不安全」,為了獲得最大程度的保護,所有其他方法也被假定為不安全。
CSRF 保護無法防止中間人攻擊,因此請將HTTPS與HTTP 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 中變更新增了對包裝非同步檢視函式的支援。
- ensure_csrf_cookie(view)¶
這個裝飾器會強制視圖傳送 CSRF Cookie。
在 Django 5.0 中變更新增了對包裝非同步檢視函式的支援。
設定¶
可以使用許多設定來控制 Django 的 CSRF 行為
常見問題¶
Django 的 CSRF 保護預設未連結到 Session 是否為問題?¶
不,這是設計使然。不將 CSRF 保護連結到 Session 允許在諸如pastebin之類的網站上使用該保護,這些網站允許來自沒有 Session 的匿名使用者的提交。
如果您希望將 CSRF 令牌儲存在使用者的 Session 中,請使用 CSRF_USE_SESSIONS
設定。
為什麼使用者在登入後可能會遇到 CSRF 驗證失敗?¶
出於安全原因,每次使用者登入時都會輪換 CSRF 令牌。任何在登入前產生的表單頁面都將具有舊的、無效的 CSRF 令牌,需要重新載入。如果使用者在登入後使用返回按鈕或在不同的瀏覽器分頁中登入,可能會發生這種情況。