手动检查Django CSRF令牌

13

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

基本上,在每个请求(全部通过POST)上,我都会检查是否存在API密钥。如果有有效的API密钥,则可以进行下一步操作。如果没有,则要回退到验证CSRF。

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


您不需要在每个请求上都进行检查,因为CSRF令牌只应该在POST和PUT请求中使用。其次,除非您在每个请求上生成它并且您的验证是可选的,否则无法验证CSRF令牌。CSRF令牌与API密钥不同,请说明您为什么需要CSRF。 - Burhan Khalid
4个回答

11

你可以通过继承CsrfViewMiddleware类并重写process_view方法来实现自定义CSRF中间件。然后用你的自定义中间件替换默认的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)。非常好的解决方案,谢谢! - InnisBrendan

6
您可以像这样使用内置的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

1
在我的情况下,“reason”始终为“None”。这在2022年仍然有效吗?(我使用基于类的视图) - Philipp S.

5

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

  1. 如果你在视图中执行测试,那么你可以直接返回 reason。根据 Django middleware的工作原理,当 process_view 返回除 None 以外的内容时,它必须是一个 HttpResponse 对象,因此它可以被视图直接返回。

    可能有些情况下你不想直接返回 reason,但如果没有任何理由不这样做,我更愿意这样做,以保持网站在其他情况下的一致性。

  2. 如果你在普通视图中使用测试,并且你已经在整个网站中使用了 CsrfViewMiddleware,那么通常情况下,request 已经通过 CsrfViewMiddleware 进行了一次传递。(是的,这种情况可能发生。我有一个情况,在这种情况下,我收到的请求被修改并重新通过 CsrfViewMiddleWare 测试,尽管它已经被 CsrfViewMiddleware 测试过了,这是由于网站范围的配置。)然而,中间件在测试完毕后会在 request 上设置 csrf_processing_done 标志,并且如果再次调用它,它将不会重新测试它,因为有了这个标志。因此,必须将 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一些原始数据。

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

from django.views.decorators.csrf import requires_csrf_token

@requires_csrf_token
def manage_trade_allocation_update(request):

在我的模板中,我添加了csrf_token生成并将其放在数据post中:
{% csrf_token %}
...
data['csrfmiddlewaretoken'] = document.querySelector('input[name="csrfmiddlewaretoken"]').value;

通过这种机制,我可以在手动 HTTP POST 请求中使用 CSRF 保护。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接