Django Rest Framework remove csrf
Asked Answered
A

17

172

I know that there are answers regarding Django Rest Framework, but I couldn't find a solution to my problem.

I have an application which has authentication and some functionality. I added a new app to it, which uses Django Rest Framework. I want to use the library only in this app. Also I want to make POST request, and I always receive this response:

{
    "detail": "CSRF Failed: CSRF token missing or incorrect."
}

I have the following code:

# urls.py
from django.conf.urls import patterns, url


urlpatterns = patterns(
    'api.views',
    url(r'^object/$', views.Object.as_view()),
)

# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from django.views.decorators.csrf import csrf_exempt


class Object(APIView):

    @csrf_exempt
    def post(self, request, format=None):
        return Response({'received data': request.data})

I want add the API without affecting the current application. So my questions is how can I disable CSRF only for this app ?

Antifriction answered 16/6, 2015 at 14:49 Comment(3)
You are already using @csrf_exempt token. You can use this on the whole view. Shouldn't that work?Shutdown
No, I still got the detail: "CSRF Failed: CSRF token missing or incorrect." message. I concluded from the answers that I should remove the default authentication.Antifriction
I was running into a VERY similar situation using Token authentication. For anyone else in the same boat: #34789801Connection
C
290

Note: Disabling CSRF is unsafe from security point of view. Please use your judgement to use the below method.

Why this error is happening?

This is happening because of the default SessionAuthentication scheme used by DRF. DRF's SessionAuthentication uses Django's session framework for authentication which requires CSRF to be checked.

When you don't define any authentication_classes in your view/viewset, DRF uses this authentication classes as the default.

'DEFAULT_AUTHENTICATION_CLASSES'= (
    'rest_framework.authentication.SessionAuthentication',
    'rest_framework.authentication.BasicAuthentication'
),

Since DRF needs to support both session and non-session based authentication to the same views, it enforces CSRF check for only authenticated users. This means that only authenticated requests require CSRF tokens and anonymous requests may be sent without CSRF tokens.

If you're using an AJAX style API with SessionAuthentication, you'll need to include a valid CSRF token for any "unsafe" HTTP method calls, such as PUT, PATCH, POST or DELETE requests.

What to do then?

Now to disable csrf check, you can create a custom authentication class CsrfExemptSessionAuthentication which extends from the default SessionAuthentication class. In this authentication class, we will override the enforce_csrf() check which was happening inside the actual SessionAuthentication.

from rest_framework.authentication import SessionAuthentication, BasicAuthentication 

class CsrfExemptSessionAuthentication(SessionAuthentication):

    def enforce_csrf(self, request):
        return  # To not perform the csrf check previously happening

In your view, then you can define the authentication_classes to be:

authentication_classes = (CsrfExemptSessionAuthentication, BasicAuthentication)

This should handle the csrf error.

Choking answered 16/6, 2015 at 18:50 Comment(10)
Sorry maybe I missed the point, but isn't a security risk to bypass/disable the csrf protection?Ferritin
@Ferritin OP needed to disable the CSRF authentication for a particular API. But yes, its a security risk to disable the csrf protection. If one needs to disable session authentication for a particular use case, then he can use this solution.Choking
Hey @RahulGupta - Is there no way to check for csrf_exempt decorator on the view, and then only disable the enforce_csrf for those views?Glider
@Glider Maybe you are looking for the the below ans by bixente57. It disables csrf for custom views.Choking
@RahulGupta if you dont want to enforce_csrf , then what will be best way ?Conchiferous
To disable csrf globally in DRF. Replace the SessionAuthentication with the above CsrfExemptSessionAuthentication in DEFAULT_AUTHENTICATION_CLASSES setting. Solve my problem. However not sure how big a risk it is.Denton
You can also make a decorator using this class to reuse it easier: def ignore_csrf(view_func): return authentication_classes([CsrfExemptSessionAuthentication])(view_func) And use @ignore_csrf instead of the @csrf_exemptPilgrimage
Where is SessionAuthentication located? Where should we put the new class?Masry
You should add a bold disclaimer at the top of your answer that this is unsafe.Declinometer
@MarkE.Haase You are right. Updated the ans to include the security disclaimer at the top too apart from the comments.Choking
F
44

Easier solution:

In views.py, use django-braces' CsrfExemptMixin and authentication_classes:

# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from django.views.decorators.csrf import csrf_exempt
from braces.views import CsrfExemptMixin


class Object(CsrfExemptMixin, APIView):
    authentication_classes = []

    def post(self, request, format=None):
        return Response({'received data': request.data})
Falkner answered 16/12, 2015 at 15:58 Comment(2)
FYI, the authentication_classes line seems to be the key. Works the same for me with or without the CsrfExemptMixin.Maria
authentication_classes = [], means there are no authentications to access entity (class/function/url), which seems perfect way to by pass any authentication.Ecclesiastic
N
21

Modify urls.py

If you manage your routes in urls.py, you can wrap your desired routes with csrf_exempt() to exclude them from the CSRF verification middleware.

import views

from django.conf.urls import patterns, url
from django.views.decorators.csrf import csrf_exempt


urlpatterns = patterns('',
    url(r'^object/$', csrf_exempt(views.ObjectView.as_view())),
    ...
)

Alternatively, as a Decorator Some may find the use of the @csrf_exempt decorator more suitable for their needs

for instance,

from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse

@csrf_exempt
def my_view(request):
    return HttpResponse('Hello world')

should get the Job Done!

Nora answered 2/8, 2018 at 17:19 Comment(4)
this worked for me with python3 and django 1.11 and seems easiest!Graphophone
The decorator does not work for me. Is there any other condition for it to work?Masry
This does not work with Django REST Framework.Everywhere
The DRF's APIView and ViewSetMixin already use the csrf_exempt decorator. Doesn't work because the enforce_csrf's call to check.process_view doesn't pass the decorated view in (there is None as the second argument).Caribou
H
18

For all who did not find a helpful answer. Yes DRF automatically removes CSRF protection if you do not use SessionAuthentication AUTHENTICATION CLASS, for example, many developers use only JWT:

'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),

But issue CSRF not set may be occurred from some another reason, for exmple you not correctly added path to you view:

url(r'^api/signup/', CreateUserView),  # <= error! DRF cant remove CSRF because it is not as_view that does it!

instead of

url(r'^api/signup/', CreateUserView.as_view()),
Hagioscope answered 1/4, 2017 at 14:14 Comment(0)
D
13

I tried a few of the answers above and felt creating a separate class was a little overboard.

For reference, I ran into this problem when trying to update a function based view method to a class based view method for user registration.

When using class-based-views (CBVs) and Django Rest Framework (DRF), Inherit from the ApiView class and set permission_classes and authentication_classes to an empty tuple. Find an example below.

class UserRegistrationView(APIView):

    permission_classes = ()
    authentication_classes = ()

    def post(self, request, *args, **kwargs):

        # rest of your code here
Descendent answered 15/3, 2019 at 3:49 Comment(0)
R
7

If you do not want to use session based authentication, you can remove Session Authentication from REST_AUTHENTICATION_CLASSES and that would automatically remove all csrf based issues. But in that case Browseable apis might not work.

Besides this error should not come even with session authentication. You should use custom authentication like TokenAuthentication for your apis and make sure to send Accept:application/json and Content-Type:application/json(provided you are using json) in your requests along with authentication token.

Ruminate answered 16/6, 2015 at 15:57 Comment(0)
A
5

You need to add this to prevent default session authentication: (settings.py)

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
    ),
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated', 
    )
}

Then: (views.py)

from rest_framework.permissions import AllowAny

class Abc(APIView):
    permission_classes = (AllowAny,)

    def ...():
Agglomerate answered 21/1, 2020 at 17:9 Comment(0)
S
4

You need to be absolutely sure, that you want to switch off CSRF protection.

  1. Create file authentication.py and place it wherever you want in your project. For example, in folder session_utils.
  2. Place this code in the file:
from rest_framework.authentication import SessionAuthentication


class SessionCsrfExemptAuthentication(SessionAuthentication):
    def enforce_csrf(self, request):
        pass

  1. When you want to make POST, PUT, PATCH or DELETE requests to your view be sure that you've changed SessionAuthentication to SessionCsrfExemptAuthentication from the new file. View example:
    @api_view(["POST"])
    @authentication_classes([SessionCsrfExemptAuthentication])
    @permission_classes([IsAuthenticated])
    def some_view(request) -> "Response":
        # some logic here
        return Response({})

This trick allow you to override method (pass) enforce_csrf and the new session authentication class will skip CSRF check.

✌️

Septennial answered 3/5, 2021 at 18:4 Comment(0)
W
2

I am struck with the same problem. I followed this reference and it worked. Solution is to create a middleware

Add disable.py file in one of your apps (in my case it is 'myapp')

class DisableCSRF(object):
    def process_request(self, request):
        setattr(request, '_dont_enforce_csrf_checks', True)

And add the middileware to the MIDDLEWARE_CLASSES

MIDDLEWARE_CLASSES = (
myapp.disable.DisableCSRF,
)
Wormeaten answered 18/11, 2015 at 11:32 Comment(1)
This will make your entire website prone to CSRF attacks. en.wikipedia.org/wiki/Cross-site_request_forgeryLaurin
M
2

My Solution is shown blow. Just decorate my class.

from django.views.decorators.csrf import csrf_exempt
@method_decorator(csrf_exempt, name='dispatch')
@method_decorator(basic_auth_required(
    target_test=lambda request: not request.user.is_authenticated
), name='dispatch')
class GenPedigreeView(View):
    pass
Mosaic answered 31/7, 2018 at 5:43 Comment(1)
While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value.Sailplane
I
1

If you are using an exclusive virtual environment for your application, you can use the following approach without effective any other applications.

What you observed happens because rest_framework/authentication.py has this code in the authenticate method of SessionAuthentication class:

self.enforce_csrf(request)

You can modify the Request class to have a property called csrf_exempt and initialize it inside your respective View class to True if you do not want CSRF checks. For example:

Next, modify the above code as follows:

if not request.csrf_exempt:
    self.enforce_csrf(request)

There are some related changes you'd have to do it in the Request class

Itchy answered 28/7, 2016 at 15:49 Comment(0)
P
1

When using REST API POSTs, absence of X-CSRFToken request header may cause that error. Django docs provide a sample code on getting and setting the CSRF token value from JS.

As pointed in answers above, CSRF check happens when the SessionAuthentication is used. Another approach is to use TokenAuthentication, but keep in mind that it should be placed first in the list of DEFAULT_AUTHENTICATION_CLASSES of REST_FRAMEWORK setting.

Povertystricken answered 20/12, 2019 at 13:6 Comment(0)
R
0

This could also be a problem during a DNS Rebinding attack.

In between DNS changes, this can also be a factor. Waiting till DNS is fully flushed will resolve this if it was working before DNS problems/changes.

Rosalinarosalind answered 21/9, 2018 at 5:45 Comment(3)
What has this got to do with the question above?Endeavor
Meaning that this problem can occur when you are switching DNS and it has not fully propagated. If the app has different routing than the Django normal session, this is why. Just informing of an edge case I ran into. This seems to be a somewhat canonical resource, so I thought I would add an additional resource.Rosalinarosalind
I don't know why this answer was downvoted after the explanation. I've been saved by people that posted their "Edge cases" on stackoverflowTessie
C
0

For me, using django 3.1.5 and django rest framework 3.12 the solution was way easier.

It happened to me that on a views.py file I had defined this two methods:

@api_view(['POST'])
@permission_classes((IsAuthenticated, ))
def create_transaction(request):
    return Response(status=status.HTTP_200_OK)

def create_transaction(initial_data):
    pass

On my urls.py:

urlpatterns = [
    path('transaction', views.create_transaction, name='transaction'),
]

Django was picking the latest and throwing the error. Renaming one of the two solved the issue.

Contrastive answered 19/6, 2021 at 17:18 Comment(0)
M
0

Code bellow would remove demand for CSRF. Even anon user would be able to send request.

from typing import List, Any

class Object(APIView):
    authentication_classes: List = []
    permission_classes: List[Any] = [AllowAny]

    ...
    ...
Milldam answered 14/4, 2022 at 14:14 Comment(0)
U
0
class MyViewSet(ViewSet):

    def initialize_request(self, request, *args, **kwargs):
        """ remove csrf protection """
        res = super(VolumeManagerViewSet, self).initialize_request(request, *args, **kwargs)
        setattr(request, '_dont_enforce_csrf_checks', True)
        return res

You can override initialize_request method to remove csrf protection.

Undrape answered 12/7, 2023 at 6:6 Comment(0)
A
-1

Removing CSRF check is not always the only (or best) solution. Actually, it's an important security mechanism for SessionAuthentication.

I was having the same issue when trying to authenticate with JWT and doing a POST request.

My initial setup looked like this:

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": (
        "rest_framework.authentication.SessionAuthentication",
        "django_cognito_jwt.JSONWebTokenAuthentication",
    ),
    ...
}

As SessionAuthentication was checked first in the list, the CSRF error was raised. My solution was as simple as changing the order to always check JWT auth first. Like this:

    "DEFAULT_AUTHENTICATION_CLASSES": (
        "django_cognito_jwt.JSONWebTokenAuthentication",
        "rest_framework.authentication.SessionAuthentication",
    ),

At the end, SessionAuthentication for me is only used in the django admin panel and 99% of the requests goes to the API that uses JWT auth.

Appoggiatura answered 3/9, 2020 at 19:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.