Django Rest Framework usuwa csrf

112

Wiem, że istnieją odpowiedzi dotyczące Django Rest Framework, ale nie mogłem znaleźć rozwiązania mojego problemu.

Mam aplikację, która ma uwierzytelnianie i pewne funkcje. Dodałem do niego nową aplikację, która korzysta z Django Rest Framework. Chcę korzystać z biblioteki tylko w tej aplikacji. Chcę również wysłać żądanie POST i zawsze otrzymuję tę odpowiedź:

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

Mam następujący kod:

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

Chcę dodać API bez wpływu na bieżącą aplikację. Więc moje pytanie brzmi: jak mogę wyłączyć CSRF tylko dla tej aplikacji?

Irene Texas
źródło
Używasz już tokena @csrf_exempt. Możesz użyć tego na całym widoku. Czy to nie powinno działać?
mukesh
Nie, nadal otrzymuję szczegóły: „Błąd CSRF: brak tokena CSRF lub jest on nieprawidłowy”. wiadomość. Z odpowiedzi wywnioskowałem, że powinienem usunąć domyślne uwierzytelnianie.
Irene Texas
1
Miałem BARDZO podobną sytuację przy użyciu uwierzytelniania tokenem. Dla każdego innego na tej samej łodzi: stackoverflow.com/questions/34789301/…
The Brewmaster

Odpowiedzi:

218

Dlaczego ten błąd się dzieje?

Dzieje się tak z powodu domyślnego SessionAuthenticationschematu używanego przez DRF. DRF SessionAuthenticationużywa struktury sesji Django do uwierzytelniania, która wymaga sprawdzenia CSRF.

Jeśli nie zdefiniujesz żadnej authentication_classesw swoim widoku / zestawie widoków, DRF używa tych klas uwierzytelniania jako domyślnych.

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

Ponieważ DRF musi obsługiwać zarówno uwierzytelnianie sesyjne, jak i nieesesyjne w tych samych widokach, wymusza sprawdzanie CSRF tylko dla uwierzytelnionych użytkowników. Oznacza to, że tylko uwierzytelnione żądania wymagają tokenów CSRF, a żądania anonimowe mogą być wysyłane bez tokenów CSRF.

Jeśli używasz interfejsu API w stylu AJAX z SessionAuthentication, musisz dołączyć prawidłowy token CSRF dla wszystkich „niebezpiecznych” wywołań metod HTTP, takich jak PUT, PATCH, POST or DELETEżądania.

Co wtedy zrobić?

Teraz, aby wyłączyć sprawdzanie csrf, możesz utworzyć niestandardową klasę uwierzytelniania, CsrfExemptSessionAuthenticationktóra wykracza poza SessionAuthenticationklasę domyślną . W tej klasie uwierzytelniania nadpisujemy enforce_csrf()sprawdzanie, które miało miejsce w pliku 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

Twoim zdaniem możesz zdefiniować authentication_classesjako:

authentication_classes = (CsrfExemptSessionAuthentication, BasicAuthentication)

To powinno obsłużyć błąd csrf.

Rahul Gupta
źródło
10
Przepraszam, może przegapiłem punkt, ale czy ominięcie / wyłączenie ochrony csrf nie stanowi zagrożenia dla bezpieczeństwa?
Paolo
1
@Paolo OP potrzebne do wyłączenia uwierzytelniania CSRF dla określonego interfejsu API. Ale tak, wyłączenie ochrony csrf jest zagrożeniem dla bezpieczeństwa. Jeśli trzeba wyłączyć uwierzytelnianie sesji dla konkretnego przypadku użycia, może skorzystać z tego rozwiązania.
Rahul Gupta
Hej @RahulGupta - czy nie ma sposobu, aby sprawdzić dekorator csrf_exempt w widoku, a następnie wyłączyć tylko egzekwowanie_csrf dla tych widoków?
Abhishek
@Abhishek Może szukasz w poniższej ans przez bixente57. Wyłącza csrf dla niestandardowych widoków.
Rahul Gupta
1
@RahulGupta, jeśli nie chcesz egzekwować_csrf, to jaki będzie najlepszy sposób?
gracz
21

Łatwiejsze rozwiązanie:

W views.py użyj nawiasów klamrowych CsrfExemptMixin i 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
źródło
1
Dzięki, to najłatwiejsze rozwiązanie problemu. Moje API przy użyciu oauth2_provider i tokena.
Dat TT
1
ahhhh człowieku. Miałem CsrfExemptMixin, ale nie miałem authentication_classes = []. Dziękuję Ci!
MagicLAMP
FYI, wiersz authentication_classes wydaje się być kluczem. Działa to samo dla mnie z lub bez CsrfExemptMixin.
Dashdrum
14

Zmodyfikuj urls.py

Jeśli zarządzasz swoimi trasami w urls.py, możesz opakować żądane trasy za pomocą csrf_exempt (), aby wykluczyć je z oprogramowania pośredniczącego weryfikacji 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())),
    ...
)

Alternatywnie, jako dekorator Niektórzy mogą uznać użycie dekoratora @csrf_exempt za bardziej odpowiednie dla ich potrzeb

na przykład,

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

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

powinien dostać pracę wykonaną!

Syed Faizan
źródło
Pewne wyjaśnienie kodu stanowiłoby lepszą odpowiedź.
chevybow
@chevybow Naprawdę przepraszam, jestem nowy w społeczności. Właściwie jest to dekorator Django, aby wyłączyć CSRF dla określonego widoku
Syed Faizan
to działało dla mnie z python3 i django 1.11 i wydaje się najłatwiejsze!
madannes
12

Dla wszystkich, którzy nie znaleźli pomocnej odpowiedzi. Tak DRF automatycznie usuwa ochronę CSRF, jeśli nie używasz SessionAuthenticationKLASY UWIERZYTELNIANIA, na przykład wielu programistów używa tylko JWT:

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

Ale problem CSRF not setmoże wystąpić z innego powodu, na przykład nieprawidłowo dodana ścieżka do widoku:

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

zamiast

url(r'^api/signup/', CreateUserView.as_view()),
Ivan Borshchov
źródło
8

Wypróbowałem kilka powyższych odpowiedzi i uznałem, że utworzenie oddzielnej klasy było trochę przesadą.

Dla porównania napotkałem ten problem, próbując zaktualizować metodę widoku opartą na funkcjach do metody widoku opartej na klasach w celu rejestracji użytkownika.

Korzystając z widoków opartych na klasach (CBV) i Django Rest Framework (DRF), dziedzicz z klasy ApiView i ustaw klasy Permissions_classes i authentication_classes na pustą krotkę. Znajdź przykład poniżej.

class UserRegistrationView(APIView):

    permission_classes = ()
    authentication_classes = ()

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

        # rest of your code here
Mike Hawes
źródło
7

Jeśli nie chcesz używać uwierzytelniania opartego na sesji, możesz usunąć Session Authenticationz REST_AUTHENTICATION_CLASSES, co spowoduje automatyczne usunięcie wszystkich problemów opartych na csrf. Ale w takim przypadku Browseable API może nie działać.

Poza tym ten błąd nie powinien występować nawet przy uwierzytelnianiu sesji. Powinieneś używać niestandardowego uwierzytelniania, takiego jak TokenAuthentication dla swojego interfejsu API i upewnij się, że wysyłasz Accept:application/jsoni Content-Type:application/json(pod warunkiem, że używasz json) w swoich żądaniach wraz z tokenem uwierzytelniania.

hspandher
źródło
4

Musisz to dodać, aby zapobiec domyślnemu uwierzytelnianiu sesji: (settings.py)

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

Następnie: (views.py)

from rest_framework.permissions import AllowAny

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

    def ...():
Nouvellie
źródło
3

Uderza mnie ten sam problem. Postępowałem zgodnie z tym odniesieniem i zadziałało. Rozwiązaniem jest stworzenie oprogramowania pośredniego

Dodaj plik disable.py w jednej ze swoich aplikacji (w moim przypadku jest to „myapp”)

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

I dodaj oprogramowanie pośrednie do MIDDLEWARE_CLASSES

MIDDLEWARE_CLASSES = (
myapp.disable.DisableCSRF,
)
Venkatesh Mondi
źródło
4
Dzięki temu cała witryna będzie podatna na ataki CSRF. en.wikipedia.org/wiki/Cross-site_request_forgery
Jeanno
1

Jeśli używasz wyłącznego środowiska wirtualnego dla swojej aplikacji, możesz zastosować następujące podejście bez efektywnych innych aplikacji.

To, co zaobserwowałeś, dzieje się, ponieważ rest_framework/authentication.pyma ten kod w authenticatemetodzie SessionAuthenticationclass:

self.enforce_csrf(request)

Możesz zmodyfikować Requestklasę tak, aby miała wywoływaną właściwość csrf_exempti zainicjować ją wewnątrz odpowiedniej klasy View, Truejeśli nie chcesz sprawdzać CSRF. Na przykład:

Następnie zmodyfikuj powyższy kod w następujący sposób:

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

Jest kilka powiązanych zmian, które musisz zrobić w Requestklasie. Pełna implementacja jest dostępna tutaj (z pełnym opisem): https://github.com/piaxis/django-rest-framework/commit/1bdb872bac5345202e2f58728d0e7fad70dfd7ed

Reetesh Ranjan
źródło
1

Moje rozwiązanie jest pokazane jak cios. Po prostu udekoruj moją klasę.

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
źródło
1
Chociaż ten kod może odpowiedzieć na pytanie, zapewnia dodatkowy kontekst dotyczący tego, dlaczego i / lub jak ten kod odpowiada, poprawia jego długoterminową wartość.
Alex Riabov
1

Podczas korzystania z REST API POST brak nagłówka żądania X-CSRFToken może powodować ten błąd. Dokumentacja Django zawiera przykładowy kod dotyczący pobierania i ustawiania wartości tokenu CSRF z JS.

Jak wskazano w odpowiedziach powyżej, sprawdzenie CSRF ma miejsce, gdy używana jest SessionAuthentication. Innym podejściem jest użycie TokenAuthentication, ale pamiętaj, że należy go umieścić na pierwszym miejscu na liście DEFAULT_AUTHENTICATION_CLASSES w ustawieniu REST_FRAMEWORK.

Alexander Kaluzhny
źródło
-1

Może to również stanowić problem podczas ataku DNS Rebinding .

Może to również mieć znaczenie między zmianami DNS. Oczekiwanie na pełne opróżnienie DNS rozwiąże ten problem, jeśli działał przed problemami / zmianami DNS.

chris Frisina
źródło
Co to ma wspólnego z powyższym pytaniem?
boatcoder
Oznacza to, że ten problem może wystąpić podczas przełączania DNS i nie został w pełni rozpowszechniony. Jeśli aplikacja ma inny routing niż normalna sesja Django, to właśnie dlatego. Po prostu informuję o skrajnym przypadku, na który wpadłem. Wydaje się, że jest to nieco kanoniczny zasób, więc pomyślałem, że dodam dodatkowy zasób.
chris Frisina