繁体   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