Django check CSRF token manually
Asked Answered
R

4

13

I am implementing an API that works either with an API key, or with a CSRF token. The goal is for it to be usable either by a web app (protected by CSRF) or by a third party application (protected by API key).

Basically on each request (all via POST), I check if there is an API key. If there is a valid one, it's good to go. If not, I want to fall back to verifying CSRF.

Is there a function I can call to verify the CSRF myself? The view itself is @csrf_exempt because API keys need to work.

Reiterant answered 5/4, 2015 at 5:6 Comment(1)
You don't need to check on each request, as CSRF tokens should only really be used on POST and PUT requests. Second, you can't verify a CSRF token unless you are generating it on each request, and your verification is optional. A CSRF token is not the same as an API key. Please clarify why you need CSRF.Leadbelly
T
11

You could probably subclass the CsrfViewMiddleware class and override the process_view method. Then include your custom middleware instead of the default CSRF one.

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(...)
Tabernacle answered 5/4, 2015 at 7:11 Comment(1)
To clarify for others, in order to use your custom middleware, go into your settings.py file for your project, delete the line in MIDDLEWARE_CLASSES django.middleware.csrf.CsrfViewMiddleware and add in your own (i.e. myApp.views.CustomCsrfMiddleware). Great solution, thanks!Luetic
M
6

You can use builtin csrf verification like this:

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
Migratory answered 5/4, 2015 at 9:8 Comment(1)
"reason" is always "None" in my case. Is this still working in 2022? (I use class based views)Pocketbook
O
5

I've been accessing the CsrfViewMiddleware like Aldarund has shown but more needs to be said about this kind of solution:

  1. If you are performing the test in a view, then you can return reason directly. According to how Django middleware works, when process_view returns something else than None, then it must be a HttpResponse object so it can just be returned by the view.

    There may be cases where you do not want to return reason directly, but if there is no reason not to do so, I'd rather return it, for consistency with how the site behaves in other cases.

  2. If you use the test in a run-of-the-mill view, and you already use CsrfViewMiddleware site-wide, it is usually the case that request will already have passed once through CsrfViewMiddleware. (Yes, it can happen. I have a case where a request I receive is modified and retested through CsrfViewMiddleWare after it has already been tested by CsrfViewMiddleware due to the site-wide configuration.) However, the middleware sets csrf_processing_done on request after it tests it and won't retest it if it is called again, because of this flag. So csrf_processing_done has to be reset to False to perform a second test.

Here's an illustration of the above:

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...
Overstrung answered 21/6, 2016 at 13:11 Comment(0)
B
4

In my case, I wanted to POST some raw data with CSRF check.

So, I use this decorator requires_csrf_token in the view which process POST data :

from django.views.decorators.csrf import requires_csrf_token

@requires_csrf_token
def manage_trade_allocation_update(request):

In my template, I added csrf_token génération and put it in data post :

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

With this mecanism, I can use CSRF protection with manual HTTP POST request.

Batter answered 23/1, 2020 at 0:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.