Django Rest Framework entfernen csrf

111

Ich weiß, dass es Antworten zum Django Rest Framework gibt, aber ich konnte keine Lösung für mein Problem finden.

Ich habe eine Anwendung, die Authentifizierung und einige Funktionen hat. Ich habe eine neue App hinzugefügt, die das Django Rest Framework verwendet. Ich möchte die Bibliothek nur in dieser App verwenden. Außerdem möchte ich eine POST-Anfrage stellen und erhalte immer folgende Antwort:

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

Ich habe folgenden 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})

Ich möchte die API hinzufügen, ohne die aktuelle Anwendung zu beeinflussen. Meine Frage ist also, wie ich CSRF nur für diese App deaktivieren kann.

Irene Texas
quelle
Sie verwenden bereits das Token @csrf_exempt. Sie können dies für die gesamte Ansicht verwenden. Sollte das nicht funktionieren?
Mukesh
Nein, ich habe immer noch das Detail: "CSRF fehlgeschlagen: CSRF-Token fehlt oder ist falsch." Botschaft. Aus den Antworten schloss ich, dass ich die Standardauthentifizierung entfernen sollte.
Irene Texas
1
Ich war mit der Token-Authentifizierung in einer sehr ähnlichen Situation. Für alle anderen im selben Boot: stackoverflow.com/questions/34789301/…
The

Antworten:

217

Warum tritt dieser Fehler auf?

Dies geschieht aufgrund des SessionAuthenticationvon DRF verwendeten Standardschemas . DRFs SessionAuthenticationverwenden das Sitzungsframework von Django zur Authentifizierung, bei dem CSRF überprüft werden muss.

Wenn Sie authentication_classesin Ihrer Ansicht / Ihrem Ansichtssatz keine definieren , verwendet DRF diese Authentifizierungsklassen als Standard.

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

Da DRF sowohl die sitzungsbasierte als auch die nicht sitzungsbasierte Authentifizierung für dieselben Ansichten unterstützen muss, wird die CSRF-Prüfung nur für authentifizierte Benutzer erzwungen. Dies bedeutet, dass nur authentifizierte Anforderungen CSRF-Token erfordern und anonyme Anforderungen ohne CSRF-Token gesendet werden können.

Wenn Sie eine AJAX-API mit SessionAuthentication verwenden, müssen Sie ein gültiges CSRF-Token für alle "unsicheren" HTTP-Methodenaufrufe, z. B. PUT, PATCH, POST or DELETEAnforderungen , einschließen .

Was ist dann zu tun?

Um die CSRF-Prüfung zu deaktivieren, können Sie jetzt eine benutzerdefinierte Authentifizierungsklasse erstellen, CsrfExemptSessionAuthenticationdie von der Standardklasse abweicht SessionAuthentication. In dieser Authentifizierungsklasse überschreiben wir die enforce_csrf()Prüfung, die innerhalb der tatsächlichen durchgeführt wurdeSessionAuthentication .

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 Ihrer Ansicht können Sie dann Folgendes definieren authentication_classes:

authentication_classes = (CsrfExemptSessionAuthentication, BasicAuthentication)

Dies sollte den csrf-Fehler behandeln.

Rahul Gupta
quelle
10
Entschuldigung, vielleicht habe ich den Punkt verpasst, aber ist es kein Sicherheitsrisiko, den CSRF-Schutz zu umgehen / zu deaktivieren?
Paolo
1
@Paolo OP musste die CSRF-Authentifizierung für eine bestimmte API deaktivieren. Aber ja, es ist ein Sicherheitsrisiko, den CSR-Schutz zu deaktivieren. Wenn die Sitzungsauthentifizierung für einen bestimmten Anwendungsfall deaktiviert werden muss, kann diese Lösung verwendet werden.
Rahul Gupta
Hey @RahulGupta - Gibt es keine Möglichkeit, in der Ansicht nach dem Dekorator csrf_exempt zu suchen und dann nur das erzwingende_csrf für diese Ansichten zu deaktivieren?
Abhishek
@Abhishek Vielleicht suchen Sie die folgenden ans von bixente57. Es deaktiviert csrf für benutzerdefinierte Ansichten.
Rahul Gupta
1
@RahulGupta Wenn Sie_csrf nicht erzwingen möchten, was ist dann der beste Weg?
Spieler
21

Einfachere Lösung:

Verwenden Sie in views.py die geschweiften Klammern CsrfExemptMixin und 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
quelle
1
Vielen Dank, dies ist die einfachste Lösung für das Problem. Meine API mit oauth2_provider und Token.
Dat TT
1
ahhhh Mann. Ich hatte CsrfExemptMixin, aber keine authentication_classes = []. Danke!
MagicLAMP
Zu Ihrer Information, die Zeile authentication_classes scheint der Schlüssel zu sein. Funktioniert bei mir mit oder ohne CsrfExemptMixin genauso.
Dashdrum
14

Ändern Sie urls.py

Wenn Sie Ihre Routen in urls.py verwalten, können Sie Ihre gewünschten Routen mit csrf_exempt () umschließen, um sie von der CSRF-Überprüfungs-Middleware auszuschließen.

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())),
    ...
)

Alternativ als Dekorateur Einige finden die Verwendung des Dekorators @csrf_exempt möglicherweise besser für ihre Bedürfnisse geeignet

zum Beispiel,

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

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

sollte den Job erledigt bekommen!

Syed Faizan
quelle
Eine Erklärung des Codes würde zu einer besseren Antwort führen.
Chevybow
@chevybow Wirklich Entschuldigung, ich bin tatsächlich neu in der Community. Eigentlich ist es ein Dekorateur von Django, CSRF für eine bestimmte Ansicht zu deaktivieren
Syed Faizan
Das hat bei mir mit Python3 und Django 1.11 funktioniert und scheint am einfachsten zu sein!
Madannes
12

Für alle, die keine hilfreiche Antwort gefunden haben. Ja, DRF entfernt automatisch den CSRF-Schutz, wenn Sie die SessionAuthenticationAUTHENTICATION CLASS nicht verwenden. Beispielsweise verwenden viele Entwickler nur JWT:

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

Das Problem CSRF not setkann jedoch auch aus einem anderen Grund auftreten. Beispielsweise haben Sie Ihrer Ansicht den Pfad nicht korrekt hinzugefügt:

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

anstatt

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

Ich habe einige der obigen Antworten ausprobiert und fand, dass das Erstellen einer separaten Klasse etwas über Bord ging.

Als Referenz stieß ich auf dieses Problem, als ich versuchte, eine funktionsbasierte Ansichtsmethode auf eine klassenbasierte Ansichtsmethode für die Benutzerregistrierung zu aktualisieren.

Wenn Sie klassenbasierte Ansichten (CBVs) und Django Rest Framework (DRF) verwenden, erben Sie von der ApiView-Klasse und setzen Sie Berechtigungsklassen und Authentifizierungsklassen auf ein leeres Tupel. Unten finden Sie ein Beispiel.

class UserRegistrationView(APIView):

    permission_classes = ()
    authentication_classes = ()

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

        # rest of your code here
Mike Hawes
quelle
7

Wenn Sie die sitzungsbasierte Authentifizierung nicht verwenden möchten, können Sie sie Session Authenticationaus REST_AUTHENTICATION_CLASSES entfernen. Dadurch werden automatisch alle csrf-basierten Probleme entfernt. In diesem Fall funktioniert Browseable apis möglicherweise nicht.

Außerdem sollte dieser Fehler auch bei der Sitzungsauthentifizierung nicht auftreten. Sie sollten benutzerdefinierte Authentifizierung wie TokenAuthentication für Ihre APIs verwenden und stellen Sie sicher , zu senden Accept:application/jsonund Content-Type:application/json(vorausgesetzt , Sie verwenden json) in Ihre Anfragen zusammen mit Authentifizierungs - Token.

hspandher
quelle
4

Sie müssen dies hinzufügen, um die Standard-Sitzungsauthentifizierung zu verhindern: (settings.py)

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

Dann: (views.py)

from rest_framework.permissions import AllowAny

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

    def ...():
Nouvellie
quelle
3

Ich bin mit dem gleichen Problem konfrontiert. Ich folgte dieser Referenz und es funktionierte. Die Lösung besteht darin, eine Middleware zu erstellen

Fügen Sie die Datei disable.py in eine Ihrer Apps ein (in meinem Fall ist es 'myapp').

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

Und fügen Sie die Mittelware zu den MIDDLEWARE_CLASSES hinzu

MIDDLEWARE_CLASSES = (
myapp.disable.DisableCSRF,
)
Venkatesh Mondi
quelle
4
Dadurch ist Ihre gesamte Website anfällig für CSRF-Angriffe. en.wikipedia.org/wiki/Cross-site_request_forgery
Jeanno
1

Wenn Sie eine exklusive virtuelle Umgebung für Ihre Anwendung verwenden, können Sie den folgenden Ansatz verwenden, ohne andere Anwendungen zu aktivieren.

Was Sie beobachtet geschieht , weil rest_framework/authentication.pydiesen Code in das hat authenticateMethode der SessionAuthenticationKlasse:

self.enforce_csrf(request)

Sie können die RequestKlasse so ändern , dass eine Eigenschaft aufgerufen wird, csrf_exemptund sie in Ihrer jeweiligen View-Klasse initialisieren, Truewenn Sie keine CSRF-Prüfungen wünschen. Beispielsweise:

Ändern Sie als Nächstes den obigen Code wie folgt:

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

Es gibt einige verwandte Änderungen, die Sie in der RequestKlasse vornehmen müssten . Eine vollständige Implementierung finden Sie hier (mit vollständiger Beschreibung): https://github.com/piaxis/django-rest-framework/commit/1bdb872bac5345202e2f58728d0e7fad70dfd7ed

Reetesh Ranjan
quelle
1

Meine Lösung wird Schlag gezeigt. Dekoriere einfach meine Klasse.

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
quelle
1
Während dieser Code die Frage möglicherweise beantwortet, verbessert die Bereitstellung eines zusätzlichen Kontexts darüber, warum und / oder wie dieser Code die Frage beantwortet, ihren langfristigen Wert.
Alex Riabov
0

Bei Verwendung von REST-API-POSTs kann das Fehlen eines X-CSRFToken-Anforderungsheaders diesen Fehler verursachen. Django-Dokumente enthalten einen Beispielcode zum Abrufen und Festlegen des CSRF-Tokenwerts von JS.

Wie in den obigen Antworten angegeben, erfolgt die CSRF-Prüfung, wenn die SessionAuthentication verwendet wird. Ein anderer Ansatz ist die Verwendung von TokenAuthentication. Beachten Sie jedoch, dass TokenAuthentication an erster Stelle in der Liste der DEFAULT_AUTHENTICATION_CLASSES der Einstellung REST_FRAMEWORK stehen sollte.

Alexander Kaluzhny
quelle
-1

Dies kann auch während eines DNS-Rebinding-Angriffs ein Problem sein .

Zwischen DNS-Änderungen kann dies auch ein Faktor sein. Wenn Sie warten, bis DNS vollständig gelöscht ist, wird dies behoben, wenn es vor DNS-Problemen / -Änderungen funktioniert hat.

Chris Frisina
quelle
Was hat das mit der obigen Frage zu tun?
Bootscodierer
Dies bedeutet, dass dieses Problem auftreten kann, wenn Sie das DNS wechseln und es sich nicht vollständig verbreitet hat. Wenn die App ein anderes Routing als die normale Django-Sitzung hat, ist dies der Grund. Ich informiere nur über einen Randfall, auf den ich gestoßen bin. Dies scheint eine etwas kanonische Ressource zu sein, daher dachte ich, ich würde eine zusätzliche Ressource hinzufügen.
Chris Frisina