1

I'm using Django Rest Framework to build a webapp with user registration/login. I'm trying to exempt the user sign up view from needing a CSRF token. This is what my view looks like right now:

class UserSignUpView(generics.CreateAPIView):
    permission_classes = [] # FIXME: doesn't seem to be working
    serializer_class = UserSerializer

    @method_decorator(csrf_exempt)
    def post(self, request, *args, **kwargs):
        super().post(self, request, *args, **kwargs)

    def get_permissions(self):
        if self.request.method == 'POST':
            return (permissions.AllowAny(), TokenHasReadWriteScope())
        return False

My settings.py looks like this:

REST_FRAMEWORK = {
    # Use Django's standard `django.contrib.auth` permissions,
    # or allow read-only access for unauthenticated users.
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ),
    'DEFAULT_PERMISSION_CLASSES': [
       'rest_framework.permissions.AllowAny',
    ]
}

I still get this on my backend output Forbidden (CSRF cookie not set.): /users/ and in the front end the classic CSRF verification failed. Request aborted.

Why wouldn't this work? Could it have something to do with the fact that I never manually set the CSRF cookie?

Jad S
  • 2,705
  • 6
  • 29
  • 49
  • get_permissions don't need a decorator csrf_exempt? You are using POST method. You can write @csrf_exempt : [Documentation](https://docs.djangoproject.com/en/1.10/ref/csrf/#django.views.decorators.csrf.csrf_protect) – Wilfried Jan 15 '17 at 01:02
  • You can also see [this post](http://stackoverflow.com/questions/17716624/django-csrf-cookie-not-set) – Wilfried Jan 15 '17 at 01:07
  • @Wilfried I tried adding `@method_decorator(csrf_exempt)` to the top of `get_permissions` but no change – Jad S Jan 15 '17 at 13:56

1 Answers1

0

Django REST framework already prevents the CSRFViewMiddleware from performing a CSRF check by using csrf_exempt on any APIView. Instead, it explicitly calls the CSRF check when a user is successfully authenticated using SessionAuthentication. You can't bypass this check, nor should you. A regular Django view may not depend on the session, in which case a CSRF attack is not possible, and you can use csrf_exempt to indicate this. When you use SessionAuthentication, you are vulnerable to CSRF attacks, and you need the check to prevent attacks. Bypassing the check in this case invariably opens you up to a vulnerability, which is why DRF does not allow you to disable the check.

You basically have two options to fix this:

  • Make sure the user it not successfully authenticated by SessionAuthentication.
  • Ensure the cookie is set, and send the token in the X-CsrfToken request header.
knbk
  • 52,111
  • 9
  • 124
  • 122
  • I don't understand how SessionAuthentication is related to CSRF protection. I see that in Django you can't have the former without the latter, but I don't get why that's the case. – Jad S Jan 15 '17 at 15:22
  • 1
    A CSRF attack is a way of abusing your session without your knowledge. If you don't have a session or similar mechanism, there's nothing an attacker can abuse by forging a cross-site request. – knbk Jan 15 '17 at 16:03
  • Thanks for the explanation. Makes perfect sense. So just to paraphrase, if authenticated with `SessionAuthentication`, `csrf_exempt` doesn't have any effect. In what scenario then would `csrf_exempt` actually be useful? According to what I've read, it's only when using `SessionAuthentification` that CSRF is checked, so using `csrf_exempt` otherwise is useless because CSRF isn't checked anyway. – Jad S Jan 15 '17 at 16:47
  • 1
    Within Django REST framework? Never. However, Django doesn't implement the same `SessionAuthentication`, so it doesn't know which views are vulnerable, and it's up to you to determine which views should be protected. In that case you can use `csrf_exempt` on views that are not vulnerable. – knbk Jan 15 '17 at 17:16