Django 手动检查 CSRF 令牌

Posted

技术标签:

【中文标题】Django 手动检查 CSRF 令牌【英文标题】:Django check CSRF token manually 【发布时间】:2015-06-09 20:21:57 【问题描述】:

我正在实现一个使用 API 密钥或 CSRF 令牌的 API。目标是让 Web 应用程序(受 CSRF 保护)或第三方应用程序(受 API 密钥保护)都可以使用它。

基本上对每个请求(全部通过 POST),我都会检查是否有 API 密钥。如果有一个有效的,那很好。如果没有,我想回退到验证 CSRF。

我可以调用一个函数来自己验证 CSRF 吗?视图本身是 @csrf_exempt,因为 API 密钥需要工作。

【问题讨论】:

您不需要检查每个请求,因为 CSRF 令牌应该只用于 POST 和 PUT 请求。其次,您无法验证 CSRF 令牌,除非您在每个请求上都生成它,并且您的验证是可选。 CSRF 令牌与 API 密钥不同。请说明为什么需要 CSRF。 【参考方案1】:

您可能可以继承 CsrfViewMiddleware 类并覆盖 process_view 方法。然后包含您的自定义中间件,而不是默认的 CSRF 中间件。

from django.middleware.csrf import CsrfViewMiddleware

class CustomCsrfMiddleware(CsrfViewMiddleware):

    def process_view(self, request, callback, callback_args, callback_kwargs):
        if request.META.get('api_key'):
            # process api key
        else:
            return super(CsrfViewMiddleware, self).process_view(...)

【讨论】:

为了向其他人澄清,为了使用您的自定义中间件,请进入您的项目的 settings.py 文件,删除 MIDDLEWARE_CLASSES django.middleware.csrf.CsrfViewMiddleware 中的行并添加您自己的(即myApp.views.CustomCsrfMiddleware )。很好的解决方案,谢谢!【参考方案2】:

您可以像这样使用内置的 csrf 验证:

from django.middleware.csrf import CsrfViewMiddleware

def check_csrf(request):
  reason = CsrfViewMiddleware().process_view(request, None, (), )
  if reason:
    # CSRF failed
    raise PermissionException() # do what you need to do here

【讨论】:

"reason" 在我的情况下始终是“无”。这在 2022 年仍然有效吗? (我使用基于类的视图)【参考方案3】:

我一直在访问CsrfViewMiddleware,就像Aldarund 显示的那样,但关于这种解决方案还有更多需要说明:

    如果您在视图中执行测试,则可以直接返回reason。根据how Django middleware works,当process_view返回的不是None,那么它必须是HttpResponse对象,所以它只能被视图返回。

    在某些情况下,您可能不想直接返回 reason,但如果没有理由不这样做,我宁愿返回它,以与网站在其他情况下的行为保持一致。

    1234563 @。 (是的,它可能会发生。我收到的请求被修改并通过CsrfViewMiddleWare 重新测试之后由于站点范围的配置,它已经被CsrfViewMiddleware 测试过。)但是,中间件在测试之后将csrf_processing_done 设置为request,并且如果再次调用它就不会重新测试它,因为这个标志。所以csrf_processing_done 必须重置为False 才能执行第二次测试。

以下是上述内容的说明:

from django.middleware.csrf import CsrfViewMiddleware

def view(request):
    request.csrf_processing_done = False
    reason = CsrfViewMiddleware().process_view(request, None, (), )
    if reason is not None:
        return reason # Failed the test, stop here.

    # process the request...

【讨论】:

【参考方案4】:

就我而言,我想通过 CSRF 检查发布一些原始数据。

所以,我在处理 POST 数据的视图中使用了这个装饰器 requires_csrf_token:

from django.views.decorators.csrf import requires_csrf_token

@requires_csrf_token
def manage_trade_allocation_update(request):

在我的模板中,我添加了 csrf_token génération 并将其放入数据帖子中:

% csrf_token %
...
data['csrfmiddlewaretoken'] = document.querySelector('input[name="csrfmiddlewaretoken"]').value;

通过这种机制,我可以将 CSRF 保护与手动 HTTP POST 请求一起使用。

【讨论】:

以上是关于Django 手动检查 CSRF 令牌的主要内容,如果未能解决你的问题,请参考以下文章

Angular2 和 Django:CSRF 令牌头痛

如何忽略发送到 Django REST 框架的 CSRF 令牌?

Django Rest Framework 将不接受我的 CSRF 令牌

在 Django 中的 ajax POST 期间被禁止(CSRF 令牌丢失或不正确。)

django csrf 令牌在每个请求上都必须是唯一的吗?

Django --CSRF 令牌丢失或不正确