Django Rest Framework rimuove csrf


112

So che ci sono risposte riguardo Django Rest Framework, ma non sono riuscito a trovare una soluzione al mio problema.

Ho un'applicazione con autenticazione e alcune funzionalità. Ho aggiunto una nuova app, che utilizza Django Rest Framework. Voglio usare la libreria solo in questa app. Inoltre voglio fare richiesta POST, e ricevo sempre questa risposta:

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

Ho il codice seguente:

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

Voglio aggiungere l'API senza influire sull'applicazione corrente. Quindi la mia domanda è: come posso disabilitare CSRF solo per questa app?


Stai già utilizzando il token @csrf_exempt. Puoi usarlo su tutta la vista. Non dovrebbe funzionare?
mukesh

No, ho ancora il dettaglio: "CSRF non riuscito: token CSRF mancante o errato". Messaggio. Ho concluso dalle risposte che avrei dovuto rimuovere l'autenticazione predefinita.
Irene Texas

1
Mi trovavo in una situazione MOLTO simile utilizzando l'autenticazione Token. Per chiunque altro nella stessa barca: stackoverflow.com/questions/34789301/…
The Brewmaster

Risposte:


218

Perché si verifica questo errore?

Ciò sta accadendo a causa dello SessionAuthenticationschema predefinito utilizzato da DRF. DRF SessionAuthenticationutilizza il framework di sessione di Django per l'autenticazione che richiede il controllo di CSRF.

Quando non ne definisci nessuna authentication_classesnella tua vista / set di visualizzazioni, DRF utilizza queste classi di autenticazione come impostazione predefinita.

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

Poiché DRF deve supportare sia l'autenticazione basata sulla sessione che quella non basata sulla sessione per le stesse viste, impone il controllo CSRF solo per gli utenti autenticati. Ciò significa che solo le richieste autenticate richiedono token CSRF e le richieste anonime possono essere inviate senza token CSRF.

Se utilizzi un'API in stile AJAX con SessionAuthentication, dovrai includere un token CSRF valido per qualsiasi chiamata al metodo HTTP "non sicura", come le PUT, PATCH, POST or DELETErichieste.

Cosa fare allora?

Ora per disabilitare il controllo csrf, puoi creare una classe di autenticazione personalizzata CsrfExemptSessionAuthenticationche si estende dalla SessionAuthenticationclasse predefinita . In questa classe di autenticazione, sovrascriveremo il enforce_csrf()controllo che stava avvenendo all'interno del file 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

A tuo avviso, puoi quindi definire authentication_classesche sia:

authentication_classes = (CsrfExemptSessionAuthentication, BasicAuthentication)

Questo dovrebbe gestire l'errore csrf.


10
Scusa, forse ho perso il punto, ma non è un rischio per la sicurezza bypassare / disabilitare la protezione CSRF?
Paolo

1
@Paolo OP necessario per disabilitare l'autenticazione CSRF per una particolare API. Ma sì, è un rischio per la sicurezza disabilitare la protezione CSRF. Se è necessario disabilitare l'autenticazione della sessione per un particolare caso d'uso, è possibile utilizzare questa soluzione.
Rahul Gupta

Ehi @RahulGupta - Non c'è modo di controllare il decoratore csrf_exempt nella vista e quindi disabilitare solo enforce_csrf per quelle viste?
Abhishek

@Abhishek Forse stai cercando il seguente ans di bixente57. Disabilita csrf per le visualizzazioni personalizzate.
Rahul Gupta

1
@RahulGupta se non vuoi enforce_csrf, allora quale sarà il modo migliore?
giocatore

21

Soluzione più semplice:

In views.py, usa le parentesi graffe CsrfExemptMixin e 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})

1
Grazie, questa è la soluzione più semplice per il problema. La mia API che utilizza oauth2_provider e token.
Dat TT

1
ahhhh uomo. Avevo CsrfExemptMixin, ma non avevo authentication_classes = []. Grazie!
MagicLAMP

Cordiali saluti, la riga authentication_classes sembra essere la chiave. Funziona allo stesso modo per me con o senza CsrfExemptMixin.
Dashdrum

14

Modifica urls.py

Se gestisci i tuoi percorsi in urls.py, puoi racchiudere i percorsi desiderati con csrf_exempt () per escluderli dal middleware di verifica 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())),
    ...
)

In alternativa, come decoratore Alcuni potrebbero trovare l'uso del decoratore @csrf_exempt più adatto alle loro esigenze

per esempio,

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

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

dovrebbe finire il lavoro!


Alcune spiegazioni al codice fornirebbero una risposta migliore.
chevybow il

@chevybow Mi dispiace davvero, in realtà sono nuovo nella community. In realtà è un Decorator of Django a disabilitare CSRF per una vista certa
Syed Faizan

questo ha funzionato per me con python3 e django 1.11 e sembra più semplice!
madannes

12

Per tutti coloro che non hanno trovato una risposta utile. Sì DRF rimuove automaticamente la protezione CSRF se non si utilizza SessionAuthenticationCLASSE DI AUTENTICAZIONE, ad esempio, molti sviluppatori utilizzano solo JWT:

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

Ma il problema CSRF not setpotrebbe essere dovuto a un altro motivo, ad esempio non hai aggiunto correttamente il percorso alla visualizzazione:

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

invece di

url(r'^api/signup/', CreateUserView.as_view()),

8

Ho provato alcune delle risposte sopra e ho sentito che creare una classe separata era un po 'esagerato.

Per riferimento, ho riscontrato questo problema durante il tentativo di aggiornare un metodo di visualizzazione basato sulla funzione a un metodo di visualizzazione basato sulla classe per la registrazione dell'utente.

Quando si utilizzano visualizzazioni basate su classi (CBV) e Django Rest Framework (DRF), ereditare dalla classe ApiView e impostare permission_classes e authentication_classes su una tupla vuota. Trova un esempio di seguito.

class UserRegistrationView(APIView):

    permission_classes = ()
    authentication_classes = ()

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

        # rest of your code here

7

Se non desideri utilizzare l'autenticazione basata sulla sessione, puoi rimuoverla Session Authenticationda REST_AUTHENTICATION_CLASSES e ciò rimuoverà automaticamente tutti i problemi basati su csrf. Ma in questo caso le API visualizzabili potrebbero non funzionare.

Inoltre questo errore non dovrebbe venire nemmeno con l'autenticazione della sessione. Dovresti usare l'autenticazione personalizzata come TokenAuthentication per le tue API e assicurarti di inviare Accept:application/jsone Content-Type:application/json(a condizione che tu stia utilizzando json) nelle tue richieste insieme al token di autenticazione.


4

È necessario aggiungerlo per impedire l'autenticazione della sessione predefinita: (settings.py)

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

Quindi: (views.py)

from rest_framework.permissions import AllowAny

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

    def ...():

3

Sono colpito dallo stesso problema. Ho seguito questo riferimento e ha funzionato. La soluzione è creare un middleware

Aggiungi il file disable.py in una delle tue app (nel mio caso è "myapp")

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

E aggiungi il middileware a MIDDLEWARE_CLASSES

MIDDLEWARE_CLASSES = (
myapp.disable.DisableCSRF,
)

4
Questo renderà il tuo intero sito web soggetto ad attacchi CSRF. en.wikipedia.org/wiki/Cross-site_request_forgery
Jeanno

1

Se stai usando un ambiente virtuale esclusivo per la tua applicazione, puoi usare il seguente approccio senza che siano efficaci altre applicazioni.

Quello che hai osservato accade perché rest_framework/authentication.pyha questo codice nel authenticatemetodo di SessionAuthenticationclasse:

self.enforce_csrf(request)

È possibile modificare la Requestclasse in modo csrf_exemptche venga chiamata una proprietà e inizializzarla all'interno della rispettiva classe View Truese non si desidera eseguire controlli CSRF. Per esempio:

Successivamente, modifica il codice sopra come segue:

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

Ci sono alcune modifiche correlate che dovresti fare durante la Requestclasse. Un'implementazione completa è disponibile qui (con la descrizione completa): https://github.com/piaxis/django-rest-framework/commit/1bdb872bac5345202e2f58728d0e7fad70dfd7ed


1

La mia soluzione è mostrata colpo. Basta decorare la mia 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

1
Sebbene questo codice possa rispondere alla domanda, fornire un contesto aggiuntivo sul perché e / o come questo codice risponde alla domanda ne migliora il valore a lungo termine.
Alex Riabov

1

Quando si utilizzano POST API REST, l'assenza dell'intestazione della richiesta X-CSRFToken può causare quell'errore. La documentazione di Django fornisce un codice di esempio su come ottenere e impostare il valore del token CSRF da JS.

Come indicato nelle risposte precedenti, il controllo CSRF viene eseguito quando viene utilizzata la SessionAuthentication. Un altro approccio consiste nell'usare TokenAuthentication, ma tieni presente che dovrebbe essere posizionato per primo nell'elenco di DEFAULT_AUTHENTICATION_CLASSES dell'impostazione REST_FRAMEWORK.


-1

Questo potrebbe anche essere un problema durante un attacco DNS Rebinding .

Tra le modifiche al DNS, anche questo può essere un fattore. Aspettare che il DNS sia completamente svuotato risolverà il problema se funzionava prima di problemi / modifiche DNS.


Cosa c'entra questo con la domanda di cui sopra?
boatcoder

Significa che questo problema può verificarsi quando si cambia DNS e non si è propagato completamente. Se l'app ha un instradamento diverso rispetto alla normale sessione di Django, questo è il motivo. Sto solo informando di un caso limite in cui mi sono imbattuto. Questa sembra essere una risorsa piuttosto canonica, quindi ho pensato di aggiungere una risorsa aggiuntiva.
chris Frisina
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.