Django Rest Framework remove csrf

111

Eu sei que existem respostas sobre Django Rest Framework, mas não consegui encontrar uma solução para o meu problema.

Tenho um aplicativo que possui autenticação e algumas funcionalidades. Eu adicionei um novo aplicativo a ele, que usa Django Rest Framework. Quero usar a biblioteca apenas neste aplicativo. Também quero fazer uma solicitação POST e sempre recebo esta resposta:

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

Eu tenho o seguinte 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})

Quero adicionar a API sem afetar o aplicativo atual. Então, minha dúvida é como posso desativar o CSRF apenas para este aplicativo?

Irene Texas
fonte
Você já está usando o token @csrf_exempt. Você pode usar isso em toda a visão. Não deveria funcionar?
mukesh
Não, ainda recebo o detalhe: "Falha no CSRF: token CSRF ausente ou incorreto." mensagem. Concluí com as respostas que devo remover a autenticação padrão.
Irene Texas
1
Eu estava passando por uma situação MUITO semelhante ao usar a autenticação Token. Para qualquer outra pessoa no mesmo barco: stackoverflow.com/questions/34789301/…
The Brewmaster

Respostas:

218

Por que esse erro está acontecendo?

Isso está acontecendo por causa do SessionAuthenticationesquema padrão usado pelo DRF. O DRF SessionAuthenticationusa a estrutura de sessão do Django para autenticação que requer que o CSRF seja verificado.

Quando você não define nenhum authentication_classesem seu view / viewset, o DRF usa essas classes de autenticação como o padrão.

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

Como o DRF precisa oferecer suporte à autenticação baseada em sessão e não-sessão para as mesmas visualizações, ele impõe a verificação CSRF apenas para usuários autenticados. Isso significa que apenas solicitações autenticadas requerem tokens CSRF e solicitações anônimas podem ser enviadas sem tokens CSRF.

Se estiver usando uma API de estilo AJAX com SessionAuthentication, você precisará incluir um token CSRF válido para todas as chamadas de método HTTP "não seguras", como PUT, PATCH, POST or DELETEsolicitações.

O que fazer então?

Agora, para desabilitar a verificação csrf, você pode criar uma classe de autenticação personalizada CsrfExemptSessionAuthenticationque se estende da SessionAuthenticationclasse padrão . Nesta classe de autenticação, iremos substituir a enforce_csrf()verificação que estava acontecendo dentro do 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

Em sua opinião, você pode definir o authentication_classescomo:

authentication_classes = (CsrfExemptSessionAuthentication, BasicAuthentication)

Isso deve lidar com o erro csrf.

Rahul Gupta
fonte
10
Desculpe, talvez eu tenha perdido o ponto, mas não é um risco de segurança ignorar / desativar a proteção csrf?
Paolo
1
@Paolo OP precisava desabilitar a autenticação CSRF para uma API específica. Mas sim, é um risco de segurança desativar a proteção csrf. Se for necessário desabilitar a autenticação de sessão para um caso de uso específico, ele poderá usar esta solução.
Rahul Gupta de
Ei @RahulGupta - Não há como verificar o decorador csrf_exempt na visualização e, em seguida, desativar apenas o enforce_csrf para essas visualizações?
Abhishek
@Abhishek Talvez você esteja procurando o abaixo ans por bixente57. Ele desativa o csrf para visualizações personalizadas.
Rahul Gupta,
1
@RahulGupta se você não quiser enforce_csrf, qual será a melhor maneira?
jogador de
21

Solução mais fácil:

Em views.py, use as chaves 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})
bixente57
fonte
1
Obrigado, esta é a solução mais fácil para o problema. Minha api usando oauth2_provider e token.
Dat TT
1
ahhhh cara. Eu tinha CsrfExemptMixin, mas não tinha authentication_classes = []. Obrigado!
MagicLAMP 01 de
FYI, a linha authentication_classes parece ser a chave. Funciona da mesma forma para mim com ou sem o CsrfExemptMixin.
Dashdrum
14

Modificar urls.py

Se você gerencia suas rotas em urls.py, pode agrupar suas rotas desejadas com csrf_exempt () para excluí-las do middleware de verificação 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())),
    ...
)

Como alternativa, como decorador, alguns podem achar o uso do decorador @csrf_exempt mais adequado para suas necessidades

por exemplo,

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

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

deve fazer o trabalho!

Syed Faizan
fonte
Alguma explicação para o código seria uma resposta melhor.
chevybow
@chevybow Sério, eu sou novo na comunidade. Na verdade, é um decorador de Django para desativar CSRF para uma determinada visão
Syed Faizan
isso funcionou para mim com python3 e django 1.11 e parece mais fácil!
Madannes
12

Para todos os que não encontraram uma resposta útil. Sim, o DRF remove automaticamente a proteção CSRF se você não usar a SessionAuthenticationAUTHENTICATION CLASS, por exemplo, muitos desenvolvedores usam apenas JWT:

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

Mas o problema CSRF not setpode ter ocorrido por algum outro motivo, por exemplo, você não adicionou corretamente o caminho para sua visualização:

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

ao invés de

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

Tentei algumas das respostas acima e senti que criar uma classe separada era um pouco exagero.

Para referência, encontrei este problema ao tentar atualizar um método de visualização baseado em função para um método de visualização baseado em classe para registro de usuário.

Ao usar visões baseadas em classes (CBVs) e Django Rest Framework (DRF), herde da classe ApiView e defina permission_classes e authentication_classes como uma tupla vazia. Encontre um exemplo abaixo.

class UserRegistrationView(APIView):

    permission_classes = ()
    authentication_classes = ()

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

        # rest of your code here
Mike Hawes
fonte
7

Se você não quiser usar autenticação baseada em sessão, você pode remover Session Authenticationde REST_AUTHENTICATION_CLASSES e isso removeria automaticamente todos os problemas baseados em csrf. Mas, nesse caso, as apis navegáveis ​​podem não funcionar.

Além disso, este erro não deve vir mesmo com autenticação de sessão. Você deve usar autenticação personalizada como TokenAuthentication para seu apis e certifique-se de enviar Accept:application/jsone Content-Type:application/json(desde que você esteja usando json) em suas solicitações junto com o token de autenticação.

Hspandher
fonte
4

Você precisa adicionar isso para evitar a autenticação de sessão padrão: (settings.py)

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

Então: (views.py)

from rest_framework.permissions import AllowAny

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

    def ...():
Nouvellie
fonte
3

Estou impressionado com o mesmo problema. Segui essa referência e funcionou. A solução é criar um middleware

Adicione o arquivo disable.py em um de seus aplicativos (no meu caso, é 'myapp')

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

E adicione o middileware ao MIDDLEWARE_CLASSES

MIDDLEWARE_CLASSES = (
myapp.disable.DisableCSRF,
)
Venkatesh Mondi
fonte
4
Isso tornará todo o seu site sujeito a ataques CSRF. en.wikipedia.org/wiki/Cross-site_request_forgery
Jeanno
1

Se estiver usando um ambiente virtual exclusivo para seu aplicativo, você pode usar a abordagem a seguir sem efetivar nenhum outro aplicativo.

O que você observou acontece porque rest_framework/authentication.pytem este código no authenticatemétodo da SessionAuthenticationclasse:

self.enforce_csrf(request)

Você pode modificar a Requestclasse para ter uma propriedade chamada csrf_exempte inicializá-la dentro de sua respectiva classe de visualização Truese não quiser verificações CSRF. Por exemplo:

Em seguida, modifique o código acima da seguinte maneira:

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

Existem algumas mudanças relacionadas que você teria que fazer na Requestaula. Uma implementação completa está disponível aqui (com a descrição completa): https://github.com/piaxis/django-rest-framework/commit/1bdb872bac5345202e2f58728d0e7fad70dfd7ed

Reetesh Ranjan
fonte
1

Minha solução é mostrada golpe. Apenas decore minha 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
fonte
1
Embora este código possa responder à pergunta, fornecer contexto adicional sobre por que e / ou como este código responde à pergunta melhora seu valor a longo prazo.
Alex Riabov
0

Ao usar POSTs da API REST, a ausência do cabeçalho de solicitação X-CSRFToken pode causar esse erro. Os documentos do Django fornecem um código de amostra para obter e configurar o valor do token CSRF do JS.

Conforme apontado nas respostas acima, a verificação CSRF ocorre quando o SessionAuthentication é usado. Outra abordagem é usar TokenAuthentication, mas lembre-se de que ele deve ser colocado primeiro na lista de DEFAULT_AUTHENTICATION_CLASSES da configuração REST_FRAMEWORK.

Alexander Kaluzhny
fonte
-1

Isso também pode ser um problema durante um ataque DNS Rebinding .

Entre as mudanças de DNS, isso também pode ser um fator. Esperar até que o DNS seja totalmente liberado resolverá isso se ele estava funcionando antes de problemas / alterações de DNS.

Chris Frisina
fonte
O que isso tem a ver com a pergunta acima?
boatcoder
O que significa que esse problema pode ocorrer quando você está alternando o DNS e ele não se propagou totalmente. Se o aplicativo tem um roteamento diferente da sessão normal do Django, é por isso. Apenas informando sobre um caso extremo que encontrei. Este parece ser um recurso um tanto canônico, então pensei em adicionar um recurso adicional.
chris Frisina