Django Rest Framework удалить csrf

113

Я знаю, что есть ответы по поводу Django Rest Framework, но я не смог найти решения своей проблемы.

У меня есть приложение с аутентификацией и некоторыми функциями. Я добавил к нему новое приложение, использующее Django Rest Framework. Я хочу использовать библиотеку только в этом приложении. Также я хочу сделать 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. Вы можете использовать это для всего вида. Разве это не должно работать?
mukesh
Нет, я все еще получил информацию: «Ошибка CSRF: токен CSRF отсутствует или неверен». сообщение. Из ответов я пришел к выводу, что мне следует удалить аутентификацию по умолчанию.
Ирен Техас
1
Я столкнулся с ОЧЕНЬ похожей ситуацией с использованием аутентификации токена. Для всех, кто находится в той же лодке: stackoverflow.com/questions/34789301/…
Brewmaster

Ответы:

219

Почему возникает эта ошибка?

Это происходит из-за 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 в представлении, а затем отключить enforce_csrf только для этих представлений?
Abhishek
@Abhishek Может быть , вы ищете ниже анс по bixente57. Он отключает csrf для пользовательских представлений.
Рахул Гупта
1
@RahulGupta, если вы не хотите enforce_csrf, что будет лучше всего?
gamer
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})
bixente57
источник
1
Спасибо, это самое простое решение проблемы. Мой api с использованием oauth2_provider и токена.
Дата TT
1
аааа мужик. У меня был CsrfExemptMixin, но не было authentication_classes = []. Спасибо!
MagicLAMP 01
К вашему сведению, строка authentication_classes кажется ключевой. У меня работает то же самое с CsrfExemptMixin или без него.
Dashdrum
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 02
@chevybow Мне очень жаль, но я новичок в сообществе. На самом деле это декоратор Django для отключения CSRF для определенного вида
Сайед Файзан
это сработало для меня с python3 и django 1.11 и кажется самым простым!
madannes
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

Я попробовал несколько ответов выше и почувствовал, что создание отдельного класса было немного за бортом.

Для справки, я столкнулся с этой проблемой при попытке обновить метод представления на основе функций до метода представления на основе классов для регистрации пользователя.

При использовании представлений на основе классов (CBV) и Django Rest Framework (DRF) выполните наследование от класса ApiView и установите свойства permission_classes и authentication_classes на пустой кортеж. Найдите пример ниже.

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. Но в этом случае API с возможностью просмотра может не работать.

Кроме того, эта ошибка не должна возникать даже при аутентификации сеанса. Вы должны использовать пользовательскую аутентификацию, такую ​​как TokenAuthentication, для своего API-интерфейса и обязательно отправлять Accept:application/jsonи Content-Type:application/json(при условии, что вы используете json) в своих запросах вместе с токеном аутентификации.

hspandher
источник
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 ...():
Nouvellie
источник
3

Меня поражает та же проблема. Я последовал этой ссылке, и это сработало. Решение - создать промежуточное ПО

Добавьте файл disable.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

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

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
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, почему и / или как этот код отвечает на вопрос, улучшает его долгосрочную ценность.
Alex Riabov
1

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

Как указано в ответах выше, проверка CSRF происходит при использовании SessionAuthentication. Другой подход - использовать TokenAuthentication, но имейте в виду, что он должен быть помещен первым в списке DEFAULT_AUTHENTICATION_CLASSES настройки REST_FRAMEWORK.

Александр Калужный
источник
1

Если вы используете эксклюзивную виртуальную среду для своего приложения, вы можете использовать следующий подход без эффективных других приложений.

То, что вы наблюдали, происходит из-за того, rest_framework/authentication.pyчто в authenticateметоде SessionAuthenticationкласса есть этот код :

self.enforce_csrf(request)

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

Затем измените приведенный выше код следующим образом:

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

Есть некоторые связанные изменения, которые вам нужно будет сделать в Requestклассе

Ритеш Ранджан
источник
-1

Это также может быть проблемой во время атаки DNS Rebinding .

В промежутках между изменениями DNS это также может быть фактором. Подождите, пока DNS не будет полностью очищен, решит эту проблему, если он работал до проблем / изменений DNS.

Крис Фризина
источник
Какое это имеет отношение к поставленному выше вопросу?
boatcoder
Это означает, что эта проблема может возникнуть, когда вы переключаете DNS, и она не распространилась полностью. Если приложение имеет другую маршрутизацию, чем обычный сеанс Django, вот почему. Просто сообщаю о крайнем случае, с которым я столкнулся. Это кажется несколько каноническим ресурсом, поэтому я подумал, что добавлю дополнительный ресурс.
Крис Фризина