Django Rest Framework eliminar csrf

111

Sé que hay respuestas con respecto a Django Rest Framework, pero no pude encontrar una solución a mi problema.

Tengo una aplicación que tiene autenticación y algunas funciones. Le agregué una nueva aplicación, que usa Django Rest Framework. Quiero usar la biblioteca solo en esta aplicación. También quiero hacer una solicitud POST, y siempre recibo esta respuesta:

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

Tengo el siguiente código:

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

Quiero agregar la API sin afectar la aplicación actual. Entonces, mi pregunta es ¿cómo puedo deshabilitar CSRF solo para esta aplicación?

Irene Texas
fuente
Ya estás usando el token @csrf_exempt. Puede usar esto en toda la vista. ¿No debería funcionar?
mukesh
No, todavía recibo el detalle: "CSRF falló: el token CSRF falta o es incorrecto". mensaje. De las respuestas llegué a la conclusión de que debería eliminar la autenticación predeterminada.
Irene Texas
1
Me estaba encontrando con una situación MUY similar al usar la autenticación Token. Para cualquier otra persona en el mismo barco: stackoverflow.com/questions/34789301/…
The Brewmaster

Respuestas:

218

¿Por qué ocurre este error?

Esto sucede debido al SessionAuthenticationesquema predeterminado utilizado por DRF. DRF SessionAuthenticationusa el marco de sesión de Django para la autenticación, que requiere que se verifique CSRF.

Cuando no define ninguna authentication_classesen su vista / conjunto de vistas, DRF usa estas clases de autenticación como las predeterminadas.

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

Dado que DRF necesita admitir autenticación basada en sesión y no sesión para las mismas vistas, aplica la verificación CSRF solo para usuarios autenticados. Esto significa que solo las solicitudes autenticadas requieren tokens CSRF y las solicitudes anónimas pueden enviarse sin tokens CSRF.

Si está utilizando una API de estilo AJAX con SessionAuthentication, deberá incluir un token CSRF válido para cualquier llamada de método HTTP "insegura", como las PUT, PATCH, POST or DELETEsolicitudes.

¿Qué hacer entonces?

Ahora, para deshabilitar la verificación csrf, puede crear una clase de autenticación personalizada CsrfExemptSessionAuthenticationque se extienda desde la SessionAuthenticationclase predeterminada . En esta clase de autenticación, anularemos la enforce_csrf()verificación que estaba sucediendo dentro del archivo real 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

En su opinión, puede definir authentication_classesque sea:

authentication_classes = (CsrfExemptSessionAuthentication, BasicAuthentication)

Esto debería manejar el error csrf.

Rahul Gupta
fuente
10
Lo siento, tal vez me perdí el punto, pero ¿no es un riesgo de seguridad omitir / deshabilitar la protección csrf?
Paolo
1
@Paolo OP necesitaba deshabilitar la autenticación CSRF para una API en particular. Pero sí, es un riesgo de seguridad desactivar la protección csrf. Si uno necesita deshabilitar la autenticación de sesión para un caso de uso particular, entonces puede usar esta solución.
Rahul Gupta
Hola @RahulGupta: ¿no hay forma de verificar el decorador csrf_exempt en la vista y luego solo deshabilitar enforce_csrf para esas vistas?
Abhishek
@Abhishek Quizás esté buscando el siguiente ans por bixente57. Deshabilita csrf para vistas personalizadas.
Rahul Gupta
1
@RahulGupta si no quiere hacer cumplir_csrf, ¿cuál será la mejor manera?
jugador
21

Solución más sencilla:

En views.py, use llaves CsrfExemptMixin y 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
fuente
1
Gracias, esta es la solución más sencilla para el problema. Mi api usando oauth2_provider y token.
Dat TT
1
ahhhh hombre. Tenía CsrfExemptMixin, pero no tenía authentication_classes = []. ¡Gracias!
MagicLAMP
FYI, la línea authentication_classes parece ser la clave. Funciona igual para mí con o sin CsrfExemptMixin.
Dashdrum
14

Modificar urls.py

Si administra sus rutas en urls.py, puede envolver sus rutas deseadas con csrf_exempt () para excluirlas del middleware de verificación 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())),
    ...
)

Alternativamente, como decorador, algunos pueden encontrar el uso del decorador @csrf_exempt más adecuado para sus necesidades

por ejemplo,

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

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

debe hacer el trabajo!

Syed Faizan
fuente
Alguna explicación del código sería una mejor respuesta.
Chevybow
@chevybow Realmente lo siento, en realidad soy nuevo en la comunidad. En realidad, es un decorador de Django para deshabilitar CSRF para una vista determinada
Syed Faizan
esto funcionó para mí con python3 y django 1.11 y parece más fácil.
madannes
12

Para todos los que no encontraron una respuesta útil. Sí, DRF elimina automáticamente la protección CSRF si no usa SessionAuthenticationAUTHENTICATION CLASS, por ejemplo, muchos desarrolladores solo usan JWT:

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

Pero el problema CSRF not setpuede ocurrir por alguna otra razón, por ejemplo, no agregó correctamente la ruta a su vista:

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

en vez de

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

Probé algunas de las respuestas anteriores y sentí que crear una clase separada era un poco exagerado.

Como referencia, me encontré con este problema al intentar actualizar un método de vista basado en funciones a un método de vista basado en clases para el registro de usuarios.

Cuando utilice vistas basadas en clases (CBV) y Django Rest Framework (DRF), herede de la clase ApiView y establezca las clases de permiso y las clases de autenticación en una tupla vacía. Encuentre un ejemplo a continuación.

class UserRegistrationView(APIView):

    permission_classes = ()
    authentication_classes = ()

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

        # rest of your code here
Mike Hawes
fuente
7

Si no desea utilizar la autenticación basada en sesión, puede eliminar Session Authenticationde REST_AUTHENTICATION_CLASSES y eso eliminaría automáticamente todos los problemas basados ​​en csrf. Pero en ese caso, las apis navegables podrían no funcionar.

Además, este error no debería venir ni siquiera con la autenticación de sesión. Debe usar autenticación personalizada como TokenAuthentication para sus apis y asegúrese de enviar Accept:application/jsony Content-Type:application/json(siempre que esté usando json) en sus solicitudes junto con el token de autenticación.

hspandher
fuente
4

Debe agregar esto para evitar la autenticación de sesión predeterminada: (settings.py)

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

Entonces: (views.py)

from rest_framework.permissions import AllowAny

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

    def ...():
Nouvellie
fuente
3

Me golpea el mismo problema. Seguí esta referencia y funcionó. La solución es crear un middleware

Agregue el archivo disable.py en una de sus aplicaciones (en mi caso es 'myapp')

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

Y agregue el middileware a MIDDLEWARE_CLASSES

MIDDLEWARE_CLASSES = (
myapp.disable.DisableCSRF,
)
Venkatesh Mondi
fuente
4
Esto hará que todo su sitio web sea propenso a ataques CSRF. en.wikipedia.org/wiki/Cross-site_request_forgery
Jeanno
1

Si está utilizando un entorno virtual exclusivo para su aplicación, puede utilizar el siguiente enfoque sin ninguna otra aplicación efectiva.

Lo que observaste sucede porque rest_framework/authentication.pytiene este código en el authenticatemétodo de SessionAuthenticationclase:

self.enforce_csrf(request)

Puede modificar la Requestclase para tener una propiedad llamada csrf_exempte inicializarla dentro de su respectiva clase de Vista Truesi no desea verificaciones CSRF. Por ejemplo:

A continuación, modifique el código anterior de la siguiente manera:

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

Hay algunos cambios relacionados que tendrías que hacer en la Requestclase. Una implementación completa está disponible aquí (con una descripción completa): https://github.com/piaxis/django-rest-framework/commit/1bdb872bac5345202e2f58728d0e7fad70dfd7ed

Reetesh Ranjan
fuente
1

Mi solución se muestra golpe. Solo decora mi clase.

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
fuente
1
Si bien este código puede responder a la pregunta, proporcionar un contexto adicional sobre por qué y / o cómo este código responde a la pregunta mejora su valor a largo plazo.
Alex Riabov
0

Cuando se utilizan POST de la API REST, la ausencia del encabezado de solicitud X-CSRFToken puede causar ese error. Los documentos de Django proporcionan un código de muestra para obtener y configurar el valor del token CSRF de JS.

Como se señaló en las respuestas anteriores, la verificación CSRF ocurre cuando se usa SessionAuthentication. Otro enfoque es usar TokenAuthentication, pero tenga en cuenta que debe colocarse primero en la lista de DEFAULT_AUTHENTICATION_CLASSES de la configuración REST_FRAMEWORK.

Alexander Kaluzhny
fuente
-1

Esto también podría ser un problema durante un ataque de reversión de DNS .

Entre los cambios de DNS, esto también puede ser un factor. Esperar hasta que el DNS se vacíe por completo resolverá esto si estaba funcionando antes de los problemas / cambios de DNS.

chris frisina
fuente
¿Qué tiene esto que ver con la pregunta anterior?
Boatcoder
Lo que significa que este problema puede ocurrir cuando cambia de DNS y no se ha propagado por completo. Si la aplicación tiene un enrutamiento diferente al de la sesión normal de Django, esta es la razón. Solo informando de un caso límite con el que me encontré. Este parece ser un recurso algo canónico, así que pensé en agregar un recurso adicional.
chris Frisina