Django Rest Framework supprimer csrf

112

Je sais qu'il y a des réponses concernant Django Rest Framework, mais je n'ai pas trouvé de solution à mon problème.

J'ai une application qui a une authentification et des fonctionnalités. J'y ai ajouté une nouvelle application, qui utilise Django Rest Framework. Je souhaite utiliser la bibliothèque uniquement dans cette application. Aussi, je veux faire une demande POST, et je reçois toujours cette réponse:

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

J'ai le code suivant:

# 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})

Je veux ajouter l'API sans affecter l'application actuelle. Ma question est donc de savoir comment désactiver CSRF uniquement pour cette application?

Irene Texas
la source
Vous utilisez déjà le jeton @csrf_exempt. Vous pouvez l'utiliser sur l'ensemble de la vue. Cela ne devrait-il pas fonctionner?
mukesh
Non, j'ai toujours le détail: "CSRF a échoué: jeton CSRF manquant ou incorrect." message. J'ai conclu à partir des réponses que je devais supprimer l'authentification par défaut.
Irene Texas
1
Je rencontrais une situation TRÈS similaire en utilisant l'authentification par jeton. Pour n'importe qui d'autre dans le même bateau: stackoverflow.com/questions/34789301/…
The Brewmaster

Réponses:

218

Pourquoi cette erreur se produit?

Cela se produit à cause du SessionAuthenticationschéma par défaut utilisé par DRF. DRF SessionAuthenticationutilise le cadre de session de Django pour l'authentification qui nécessite que CSRF soit vérifié.

Lorsque vous n'en définissez aucune authentication_classesdans votre vue / ensemble de vues, DRF utilise ces classes d'authentification par défaut.

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

Étant donné que DRF doit prendre en charge à la fois l'authentification basée sur la session et l'authentification non-session pour les mêmes vues, il applique la vérification CSRF uniquement pour les utilisateurs authentifiés. Cela signifie que seules les demandes authentifiées nécessitent des jetons CSRF et que les demandes anonymes peuvent être envoyées sans jetons CSRF.

Si vous utilisez une API de style AJAX avec SessionAuthentication, vous devrez inclure un jeton CSRF valide pour tous les appels de méthode HTTP "non sécurisés", tels que les PUT, PATCH, POST or DELETErequêtes.

Que faire alors?

Maintenant, pour désactiver la vérification csrf, vous pouvez créer une classe d'authentification personnalisée CsrfExemptSessionAuthenticationqui s'étend à partir de la SessionAuthenticationclasse par défaut . Dans cette classe d'authentification, nous remplacerons le enforce_csrf()contrôle qui se produisait dans le réel 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

À votre avis, vous pouvez alors définir le authentication_classescomme étant:

authentication_classes = (CsrfExemptSessionAuthentication, BasicAuthentication)

Cela devrait gérer l'erreur csrf.

Rahul Gupta
la source
10
Désolé, j'ai peut-être manqué le point, mais le contournement / la désactivation de la protection csrf n'est-il pas un risque de sécurité?
Paolo
1
@Paolo OP avait besoin de désactiver l'authentification CSRF pour une API particulière. Mais oui, c'est un risque pour la sécurité de désactiver la protection csrf. Si l'on a besoin de désactiver l'authentification de session pour un cas d'utilisation particulier, il peut utiliser cette solution.
Rahul Gupta
Hey @RahulGupta - N'y a-t-il aucun moyen de vérifier le décorateur csrf_exempt sur la vue, puis de désactiver uniquement le enforce_csrf pour ces vues?
Abhishek
@Abhishek Vous recherchez peut-être le ci-dessous et par bixente57. Il désactive csrf pour les vues personnalisées.
Rahul Gupta
1
@RahulGupta si vous ne voulez pas appliquer_csrf, quel sera le meilleur moyen?
joueur
21

Solution plus simple:

Dans views.py, utilisez les accolades CsrfExemptMixin et 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})
bixente57
la source
1
Merci, c'est la solution la plus simple à ce problème. Mon API utilisant oauth2_provider et token.
Dat TT
1
ahhhh homme. J'avais CsrfExemptMixin, mais je n'avais pas authentication_classes = []. Je vous remercie!
MagicLAMP
Pour info, la ligne authentication_classes semble être la clé. Fonctionne de la même manière pour moi avec ou sans le CsrfExemptMixin.
Dashdrum
14

Modifier urls.py

Si vous gérez vos routes dans urls.py, vous pouvez encapsuler vos routes souhaitées avec csrf_exempt () pour les exclure du middleware de vérification CSRF.

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

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

Alternativement, en tant que décorateur Certains peuvent trouver l'utilisation du décorateur @csrf_exempt plus adapté à leurs besoins

par exemple,

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

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

devrait faire le travail!

Syed Faizan
la source
Une explication du code ferait une meilleure réponse.
chevybow
@chevybow Vraiment désolé, je suis en fait nouveau dans la communauté. En fait, c'est un décorateur de Django pour désactiver CSRF pour une certaine vue
Syed Faizan
cela a fonctionné pour moi avec python3 et django 1.11 et semble le plus simple!
madannes
12

Pour tous ceux qui n'ont pas trouvé de réponse utile. Oui DRF supprime automatiquement la protection CSRF si vous n'utilisez pas SessionAuthenticationAUTHENTICATION CLASS, par exemple, de nombreux développeurs n'utilisent que JWT:

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

Mais le problème CSRF not setpeut provenir d'une autre raison, par exemple, vous n'avez pas correctement ajouté le chemin à votre vue:

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

au lieu de

url(r'^api/signup/', CreateUserView.as_view()),
Ivan Borshchov
la source
8

J'ai essayé quelques-unes des réponses ci-dessus et j'ai senti que créer une classe séparée était un peu exagéré.

Pour référence, j'ai rencontré ce problème en essayant de mettre à jour une méthode de vue basée sur une fonction vers une méthode de vue basée sur une classe pour l'enregistrement des utilisateurs.

Lorsque vous utilisez des vues basées sur les classes (CBV) et Django Rest Framework (DRF), héritez de la classe ApiView et définissez permission_classes et authentication_classes sur un tuple vide. Trouvez un exemple ci-dessous.

class UserRegistrationView(APIView):

    permission_classes = ()
    authentication_classes = ()

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

        # rest of your code here
Mike Hawes
la source
7

Si vous ne souhaitez pas utiliser l'authentification basée sur la session, vous pouvez supprimer Session Authenticationde REST_AUTHENTICATION_CLASSES et cela supprimerait automatiquement tous les problèmes basés sur csrf. Mais dans ce cas, les API navigables peuvent ne pas fonctionner.

En outre, cette erreur ne devrait pas venir même avec l'authentification de session. Vous devez utiliser une authentification personnalisée telle que TokenAuthentication pour vos API et assurez-vous d'envoyer Accept:application/jsonet Content-Type:application/json(à condition que vous utilisez json) dans vos demandes avec le jeton d'authentification.

hspandher
la source
4

Vous devez ajouter ceci pour empêcher l'authentification de session par défaut: (settings.py)

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

Ensuite: (views.py)

from rest_framework.permissions import AllowAny

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

    def ...():
Nouvellie
la source
3

Je suis frappé du même problème. J'ai suivi cette référence et cela a fonctionné. La solution est de créer un middleware

Ajouter le fichier disable.py dans l'une de vos applications (dans mon cas, c'est 'myapp')

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

Et ajoutez le middileware aux MIDDLEWARE_CLASSES

MIDDLEWARE_CLASSES = (
myapp.disable.DisableCSRF,
)
Venkatesh Mondi
la source
4
Cela rendra l'ensemble de votre site Web sujet aux attaques CSRF. en.wikipedia.org/wiki/Cross-site_request_forgery
Jeanno
1

Si vous utilisez un environnement virtuel exclusif pour votre application, vous pouvez utiliser l'approche suivante sans aucune autre application efficace.

Ce que vous avez observé se produit car rest_framework/authentication.pyce code est dans la authenticateméthode de SessionAuthenticationclasse:

self.enforce_csrf(request)

Vous pouvez modifier la Requestclasse pour avoir une propriété appelée csrf_exemptet l'initialiser dans votre classe View respective Truesi vous ne voulez pas de vérifications CSRF. Par exemple:

Ensuite, modifiez le code ci-dessus comme suit:

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

Il y a des changements connexes que vous devrez faire en Requestclasse. Une implémentation complète est disponible ici (avec une description complète): https://github.com/piaxis/django-rest-framework/commit/1bdb872bac5345202e2f58728d0e7fad70dfd7ed

Reetesh Ranjan
la source
1

Ma solution est montrée coup. Décorez simplement ma classe.

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
Jak Liao
la source
1
Bien que ce code puisse répondre à la question, fournir un contexte supplémentaire concernant la raison et / ou la manière dont ce code répond à la question améliore sa valeur à long terme.
Alex Riabov
1

Lors de l'utilisation de POST API REST, l'absence d'en-tête de requête X-CSRFToken peut provoquer cette erreur. Documents Django fournit un exemple de code sur l'obtention et la définition de la valeur du jeton CSRF à partir de JS.

Comme indiqué dans les réponses ci-dessus, la vérification CSRF se produit lorsque la SessionAuthentication est utilisée. Une autre approche consiste à utiliser TokenAuthentication, mais gardez à l'esprit qu'il doit être placé en premier dans la liste des DEFAULT_AUTHENTICATION_CLASSES du paramètre REST_FRAMEWORK.

Alexander Kaluzhny
la source
-1

Cela pourrait également être un problème lors d'une attaque de reliure DNS .

Entre les changements DNS, cela peut également être un facteur. Attendre que le DNS soit complètement vidé résoudra ce problème s'il fonctionnait avant les problèmes / changements DNS.

Chris Frisina
la source
Qu'est-ce que cela a à voir avec la question ci-dessus?
boatcoder
Cela signifie que ce problème peut se produire lorsque vous changez de DNS et qu'il ne s'est pas entièrement propagé. Si l'application a un routage différent de la session normale de Django, c'est pourquoi. Je viens d'informer d'un cas de pointe que j'ai rencontré. Cela semble être une ressource quelque peu canonique, alors j'ai pensé ajouter une ressource supplémentaire.
chris Frisina