簡體   English   中英

Django (DRF) & React - 禁止(未設置 CSRF cookie)

[英]Django (DRF) & React - Forbidden (CSRF cookie not set)

有數十個問題與我要問的問題基本相同。 但是,他們的答案似乎都不適合我。

我有一個 React 前端,我在其中使用axios將請求發送到后端。 例子
const request = await axios.post('${BASE_URL}/logout/')

大多數 Django Rest 框架端點都是用 ViewSet 制作的。 但是,我有一些是定制的,主要用於身份驗證。

path('createaccount/', views.create_account),
path('me/', views.current_user),
path('logout/', views.logout),
path('login/', views.login),
path('resetpassword', views.reset_password),

為了開發這個項目,我在這些視圖上方包含@csrf_exempt ,因為我當時不想處理它。 現在我接近部署,是時候弄清楚了。

一些答案說我需要從 Django 獲取一個 CSRF 令牌,它存儲在 cookies 中,我需要在每個請求的 header 中傳遞它。 一些答案說我需要做的就是像這樣配置 axios

axios.defaults.xsrfHeaderName = "X-CSRFTOKEN";
axios.defaults.xsrfCookieName = "XCSRF-TOKEN";

它會“正常工作”。 我嘗試將我的CSRF_COOKIE_NAME調整為各種值以使其也能正常工作。

有些答案甚至說要保留@csrf_exempt但這聽起來是一個非常非常糟糕的主意。

我真的需要生成/獲取 CSRF cookie 嗎? 我是否將它包含在每個請求中? 或者它只是axios的配置?

我曾經使用令牌身份驗證來解決這個問題。 我就是這樣解決的。 但不確定是否是最好的主意。 我僅將csrf_exempt用於此視圖,而所有其他視圖均為視圖集。

@csrf_exempt
def get_current_user(request, *args, **kwargs):
    if request.method == 'GET':
        user = request.user
        serializer = UserDataSerializer(user)
        return JsonResponse(serializer.data, safe=False)

我在settings.py中的中間件

MIDDLEWARE = [
   'django.middleware.security.SecurityMiddleware',
   'django.contrib.sessions.middleware.SessionMiddleware',
   'corsheaders.middleware.CorsMiddleware',
   # 'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
   'django.middleware.common.CommonMiddleware',
   'django.middleware.csrf.CsrfViewMiddleware',
   'django.contrib.auth.middleware.AuthenticationMiddleware',
   'django.middleware.locale.LocaleMiddleware',
   'oauth2_provider.middleware.OAuth2TokenMiddleware',
   'django.contrib.messages.middleware.MessageMiddleware',
   'django.middleware.clickjacking.XFrameOptionsMiddleware',
   'auditlog.middleware.AuditlogMiddleware',
]

為了使 CSRF 保護工作,您需要從 Django 發送 CSRF cookie 到 React 作為對某些請求(如登錄或其他)的響應。 它將在前端使用 Set-Cookie 設置 cookie。 因此,請確保您有一個在 Django 端執行此操作的視圖。 如果沒有,請創建一個視圖作為響應生成該令牌。

Django (4.04) CSRF 驗證如何工作(簡化,基於 middleware/csrf.py):

  1. 從 cookie 獲取 CSRF 令牌(因此前端需要在另一個請求時重新發送它)——它也可能從 session 獲取它,但在 React 的情況下我不會使用它

    def _get_token(self, request): .... try: cookie_token = request.COOKIES[settings.CSRF_COOKIE_NAME] except KeyError: return None
  2. 將該 cookie CSRF 令牌與來自請求的非 cookie 令牌進行比較:

     def _check_token(self, request): # Access csrf_token via self._get_token() as rotate_token() may have # been called by an authentication middleware during the # process_request() phase. try: csrf_token = self._get_token(request) except InvalidTokenFormat as exc: raise RejectRequest(f"CSRF cookie {exc.reason}.") if csrf_token is None: # No CSRF cookie. For POST requests, we insist on a CSRF cookie, # and in this way we can avoid all CSRF attacks, including login # CSRF. raise RejectRequest(REASON_NO_CSRF_COOKIE) # Check non-cookie token for match. request_csrf_token = "" if request.method == "POST": try: request_csrf_token = request.POST.get("csrfmiddlewaretoken", "") except UnreadablePostError: # Handle a broken connection before we've completed reading the # POST data. process_view shouldn't raise any exceptions, so # we'll ignore and serve the user a 403 (assuming they're still # listening, which they probably aren't because of the error). pass if request_csrf_token == "": # Fall back to X-CSRFToken, to make things easier for AJAX, and # possible for PUT/DELETE. try: request_csrf_token = request.META[settings.CSRF_HEADER_NAME] except KeyError: raise RejectRequest(REASON_CSRF_TOKEN_MISSING) token_source = settings.CSRF_HEADER_NAME else: token_source = "POST" try: request_csrf_token = _sanitize_token(request_csrf_token) except InvalidTokenFormat as exc: reason = self._bad_token_message(exc.reason, token_source) raise RejectRequest(reason) if not _does_token_match(request_csrf_token, csrf_token): reason = self._bad_token_message("incorrect", token_source) raise RejectRequest(reason)

如您所見,您要么需要在 POST 請求中包含csrfmiddlewaretoken ,要么將其包含在 header 中,鍵為: settings.CSRF_HEADER_NAME ,值從前端的 cookies 讀取。

因此,例如,您設置withCredentials: true (以包含初始 cookie),在 React 中讀取該初始 CSRF cookie,並在特定鍵的 axios 請求中添加到 header。

如有疑問,我只會在 middleware/csrf.py 中的 Django 代碼中調試請求設置斷點,您可以跟蹤缺少的內容以及 CSRF 驗證失敗的原因。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM