csrf_exempt 所有回调均为 true

问题描述 投票:0回答:1

我为我的 django Rest api 创建了一些自定义中间件,以强制设置自定义标头的 CSRF 安全策略。我对 CsrfViewMiddleware 进行了子类化并重写了一些方法来满足我的需求,最值得注意的是 check_token 方法。然而,我后来注意到所有请求都将属性“csrf_exempt”设置为 true,即使我没有使用 csrf_exempt 装饰器。我搜索了 django 文档,试图找出为什么会发生这种情况,但没有成功。这是我的自定义中间件:

class CustomCsrfMiddleware(CsrfViewMiddleware):

    def _reject(self, request, reason):
        logger.warning("Forbidden (%s): %s", reason, request.path)
        return Response(
            {'detail': 'Forbidden', 'reason': reason},
            status.HTTP_403_FORBIDDEN
        )

    def _check_token(self, request):

        # Get the two relevant pieces of information, the
        # the returned csrftoken in the custom header and the hmac
        try:
            hmac = _get_hmac(request)
        except BadDigest as e:
            raise RejectRequest(e.reason)

        try:
            request_csrf_token = request.META[settings.CSRF_HEADER_NAME]
            token_source = settings.CSRF_HEADER_NAME
        except KeyError:
            raise RejectRequest(REASON_CSRF_TOKEN_MISSING)

        try:
            _check_token_format(request_csrf_token)
        except InvalidTokenFormat as e:
            reason = self._bad_token_message(e.reason, token_source)
            raise RejectRequest(reason)

        if not _does_match(request_csrf_token, hmac):
            reason = self._bad_token_message("incorrect", token_source)
            raise RejectRequest(reason)

    def process_request(self, request):

        try:
            csrf_secret = self._get_secret(request)
            if csrf_secret is None:
                raise InvalidTokenFormat(REASON_CSRF_TOKEN_MISSING)
        except InvalidTokenFormat:
            _add_new_csrf_cookie(request)
        else:
            if csrf_secret is not None:
                request.META["CSRF_COOKIE"] = csrf_secret

    def process_view(self, request, callback, callback_args, callback_kwargs):
        # exempt /hook paths
        print('callback: ', getattr(callback, "csrf_exempt", False))

        return super().process_view(request, callback, callback_args, callback_kwargs)

还有我的 CSRF 设置:

CSRF_COOKIE_NAME = 'csrftoken'
CSRF_COOKIE_AGE = 60 * 60 * 24 * 30  # 1 month
CSRF_SAMESITE = 'lax'
CSRF_COOKIE_SECURE = True
CSRF_TRUSTED_ORIGINS = ['https://localhost:3000']
CSRF_COOKIE_HTTPONLY = False
CSRF_HEADER_NAME = 'HTTP_X_CSRFTOKEN'
CSRF_COOKIE_DOMAIN = 'localhost'
django csrf middleware
1个回答
0
投票

我发现了问题。事实证明,django Rest框架将所有视图包装在APIView的as_view方法的返回语句中的csrf_exempt中。因此,我的解决方案是创建另一个名为 csrf_ignore 的装饰器,在名为 csrf_ignore 的视图上设置一个新属性。


from functools import wraps


def csrf_ignore(view_func):
    """Mark a view function as being exempt from the CSRF view protection."""

    # view_func.csrf_exempt = True would also work, but decorators are nicer
    # if they don't have side effects, so return a new function.
    @wraps(view_func)
    def wrapper_view(*args, **kwargs):
        return view_func(*args, **kwargs)

    wrapper_view.csrf_ignore = True
    return wrapper_view

然后在我的中间件的process_view函数中添加一些逻辑:

    def process_view(self, request, callback, callback_args, callback_kwargs):
        if not getattr(callback, 'csrf_ignore', False):
            setattr(callback, 'csrf_exempt', False)

        return super().process_view(request, callback, callback_args, callback_kwargs)

现在正确的属性已设置,中间件将在需要时检查 csrf 保护。

© www.soinside.com 2019 - 2024. All rights reserved.