Django Rest Framework видалити csrf


111

Я знаю, що є відповіді щодо Django Rest Framework, але я не зміг знайти рішення своєї проблеми.

У мене є програма, яка має автентифікацію та певну функціональність. До нього я додав нову програму, яка використовує рамку відпочинку Django. Я хочу використовувати бібліотеку лише в цьому додатку. Також я хочу зробити POST-запит, і я завжди отримую цю відповідь:

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

У мене є такий код:

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

Я хочу додати API, не впливаючи на поточну програму. Тож мої запитання - як я можу відключити CSRF лише для цього додатка?


Ви вже використовуєте маркер @csrf_exempt. Ви можете використовувати це на всьому поданні. Чи не повинно це працювати?
мукеш

Ні, я все ще отримав детальну інформацію: "CSRF Failed: CSRF маркер відсутній або неправильний." повідомлення. З відповідей я зробив висновок, що слід видалити автентифікацію за замовчуванням.
Ірен Техас

1
Я зіткнувся з ДУЖЕ подібною ситуацією, використовуючи аутентифікацію Token. Для всіх, хто знаходиться в тому ж човні: stackoverflow.com/questions/34789301/…
The Brewmaster

Відповіді:


218

Чому трапляється ця помилка?

Це відбувається через SessionAuthenticationсхему за замовчуванням, яку використовує DRF. DRF SessionAuthenticationвикористовує рамку сеансу Django для аутентифікації, що вимагає перевірки CSRF.

Якщо ви не визначаєте жодного authentication_classesу своєму представленні / перегляді, DRF використовує ці класи аутентифікації за замовчуванням.

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

Оскільки DRF повинен підтримувати аутентифікацію, засновану на сеансах та несесіях, для одних і тих же переглядів, він примушує перевірити CSRF лише для аутентифікованих користувачів. Це означає, що лише автентифіковані запити вимагають маркерів CSRF, а анонімні запити можуть надсилатися без маркерів CSRF.

Якщо ви використовуєте API стилю AJAX з SessionAuthentication, вам потрібно буде включити дійсний маркер CSRF для будь-яких "небезпечних" дзвінків методу HTTP, наприклад PUT, PATCH, POST or DELETEзапитів.

Що робити тоді?

Тепер, щоб відключити перевірку csrf, ви можете створити спеціальний клас аутентифікації, CsrfExemptSessionAuthenticationякий поширюється на SessionAuthenticationклас за замовчуванням . У цьому класі аутентифікації ми скасуємо enforce_csrf()перевірку, що відбувалася всередині фактичного 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

На ваш погляд, тоді ви можете визначити authentication_classes:

authentication_classes = (CsrfExemptSessionAuthentication, BasicAuthentication)

Це має вирішити помилку csrf.


10
Вибачте, можливо, я пропустив пункт, але чи не є ризик безпеки для обходу / відключення захисту csrf?
Паоло

1
@ Paolo OP потрібно було вимкнути аутентифікацію CSRF для певного API. Але так, це небезпека безпеки для відключення захисту csrf. Якщо потрібно відключити аутентифікацію сеансу для конкретного випадку використання, він може використовувати це рішення.
Рахул Гупта

Привіт @RahulGupta - Чи немає способу перевірити csrf_exempt decorator на поданні, а потім вимкнути лише Execute_csrf для цих подань?
Абхішек

@Abhishek Можливо, ви шукаєте наведені нижче ans від bixente57. Це вимикає csrf для користувацьких переглядів.
Рахул Гупта

1
@RahulGupta, якщо ви не хочете примусово виконувати_csrf, то який буде найкращий спосіб?
геймер

21

Простіше рішення:

У Views.py використовуйте дужки CsrfExemptMixin та 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
Дякую, це найпростіше рішення питання. Мій api використовує oauth2_provider та токен.
Dat TT

1
А-а-а-а-людина. У мене був CsrfExemptMixin, але у мене не було автентифікації_classes = []. Дякую!
MagicLAMP

FYI, здається, що ключовим є рядок authentication_classes. Для мене працює те саме, з CsrfExemptMixin або без нього.
Дашдрум

14

Змініть urls.py

Якщо ви керуєте своїми маршрутами в urls.py, ви можете обернути потрібні маршрути csrf_exempt (), щоб виключити їх із середнього програмного забезпечення для перевірки 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())),
    ...
)

Крім того, як декоратор Деякі можуть вважати використання декоратора @csrf_exempt більш підходящим для їх потреб

наприклад,

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

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

має отримати роботу, виконану!


Деякі пояснення коду допоможуть краще відповісти.
chevybow

@chevybow Дуже вибачте, я насправді новачок у спільноті. Насправді це декоратор Джанго, щоб вимкнути CSRF для певного виду
Syed Faizan

це працювало для мене з python3 та django 1,11 і, здається, найлегше!
маданні

12

Для всіх, хто не знайшов корисної відповіді. Так, DRF автоматично знімає захист CSRF, якщо ви не використовуєте SessionAuthenticationАВТОМЕНТИЧНИЙ КЛАС, наприклад, багато розробників використовують тільки JWT:

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

Але проблема CSRF not setможе виникнути з іншої причини, для прикладу ви неправильно додали шлях до перегляду:

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

замість

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

8

Я спробував декілька відповідей вище і відчув, що створення окремого класу було трохи за бортом.

Для довідки я зіткнувся з цією проблемою, намагаючись оновити метод перегляду на основі функцій до методу перегляду на основі класу для реєстрації користувача.

Використовуючи перегляди на основі класів (CBVs) і Django Rest Framework (DRF), успадкуйте з класу ApiView і встановіть дозволені_класи та класи автентифікації_класини на порожній кортеж. Знайдіть приклад нижче.

class UserRegistrationView(APIView):

    permission_classes = ()
    authentication_classes = ()

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

        # rest of your code here

7

Якщо ви не хочете використовувати аутентифікацію на основі сеансу, ви можете її видалити Session Authentication з REST_AUTHENTICATION_CLASSES, що автоматично видалить усі проблеми, що базуються на csrf. Але в такому випадку аглядальна апліка може не працювати.

Крім того, ця помилка не повинна виникати навіть при аутентифікації сеансу. Ви повинні використовувати власну автентифікацію, таку як TokenAuthentication для свого apis, і обов'язково надсилайте Accept:application/jsonта Content-Type:application/json(за умови, що ви використовуєте json) у своїх запитах разом із маркером аутентифікації.


4

Вам потрібно додати це, щоб запобігти автентифікації сеансу за замовчуванням: (settings.py)

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

Потім: (views.py)

from rest_framework.permissions import AllowAny

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

    def ...():

3

Мене вражає однакова проблема. Я дотримувався цієї довідки і вона спрацювала. Рішення - створити проміжне програмне забезпечення

Додайте файл invalid.py в одне із своїх додатків (у моєму випадку це "myapp")

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

І додайте проміжне програмне забезпечення до MIDDLEWARE_CLASSES

MIDDLEWARE_CLASSES = (
myapp.disable.DisableCSRF,
)

4
Це зробить весь ваш сайт схильним до атак CSRF. en.wikipedia.org/wiki/Cross-site_request_forgery
Жанно

1

Якщо ви використовуєте для своєї програми ексклюзивне віртуальне середовище, ви можете використовувати наступний підхід без ефективних інших програм.

Те, що ви спостерігали, відбувається через rest_framework/authentication.pyте, що в authenticateметоді SessionAuthenticationкласу є цей код :

self.enforce_csrf(request)

Ви можете змінити Requestклас, щоб мати властивість викликається, csrf_exemptі ініціалізувати його у відповідному класі ViewTrue якщо ви не хочете перевірки CSRF. Наприклад:

Далі змініть наведений вище код таким чином:

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

Існує кілька пов'язаних змін, які вам доведеться зробити в Requestкласі. Повна реалізація доступна тут (з повним описом): https://github.com/piaxis/django-rest-framework/commit/1bdb872bac5345202e2f58728d0e7fad70dfd7ed


1

Моє рішення показано ударом. Просто прикрасьте мій клас.

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
Хоча цей код може відповісти на питання, надаючи додатковий контекст стосовно того, чому та / або як цей код відповідає на питання, покращує його довгострокове значення.
Олексій Рябов

1

Під час використання POST API REST відсутність заголовка запиту X-CSRFToken може спричинити цю помилку. Документи Django надають зразок коду щодо отримання та встановлення значення маркера CSRF з JS.

Як зазначено у відповідях вище, перевірка CSRF відбувається, коли використовується SessionAuthentication. Інший підхід полягає у використанні TokenAuthentication, але майте на увазі, що його слід розмістити на першому місці у списку DEFAULT_AUTHENTICATION_CLASSES налаштування REST_FRAMEWORK.


-1

Це також може бути проблемою під час атаки перезаписування DNS .

Між змінами DNS це також може бути фактором. Якщо чекати, поки DNS повністю розмивається, це вирішить, якщо він працював до проблем / змін DNS.


Що це стосується вищезазначеного питання?
boatcoder

Це означає, що ця проблема може виникнути, коли ви перемикаєте DNS і вона не повністю розповсюджується. Якщо програма має іншу маршрутизацію, ніж звичайна сесія Django, саме тому. Тільки повідомивши про крайній випадок, на який я зіткнувся. Це здається дещо канонічним ресурсом, тому я думав, що додаю додатковий ресурс.
Кріс Фрізіна
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.