Проверка CSRF-верификации с помощью Django Rest Framework
Я использую Django Rest Framework 3 и хотел бы проверить проверку CSRF.
Сначала я инициализирую DRF APIClient
:
client = APIClient(enforce_csrf_checks=True)
Затем я устанавливаю пароль для пользователя, чтобы я мог войти и получить сеанс:
superuser.set_password('1234')
superuser.save()
client.login(email=superuser.email, password='1234')
Теперь нам нужен токен CSRF. Для этого я просто создаю запрос и извлекаю токен из куки.
response = client.request()
csrftoken = client.cookies['csrftoken'].value
При проверке кода, кажется, это работает, я получаю верный токен CSRF. Затем я делаю запрос POST, передавая в csrfmiddlewartoken
параметр:
data = {'name': 'My fancy test report', 'csrfmiddlewaretoken': csrftoken}
response = client.post(API_BASE + '/reports', data=data, format='json')
assert response.status_code == status.HTTP_201_CREATED, response.content
Проблема в том, что это не удается:
tests/api/test_api.py:156: in test_csrf_success
assert response.status_code == status.HTTP_201_CREATED, response.content
E AssertionError: {"detail":"CSRF Failed: CSRF token missing or incorrect."}
E assert 403 == 201
E + where 403 = <rest_framework.response.Response object at 0x7f7bd6453bd0>.status_code
E + and 201 = status.HTTP_201_CREATED
Как правильно проверить проверку CSRF с помощью DRF?
1 ответ
РЕДАКТИРОВАТЬ
Итак, после небольшого исследования я обнаружил следующее:
Django не обязательно будет устанавливать токен CSRF в заголовке, если он не рендерит шаблон, который явно имеет csrf_token
тег шаблона включен. Это означает, что вам нужно запросить страницу, которая отображает форму с токеном csrf, или вам нужно создать представление с запросом токена, которое украшено ensure_csrf_cookie
,
Поскольку токен csrf является уникальным для каждого сеанса, можно создать общее представление настройки токена, которое выглядит примерно так:
from django.views.decorators.csrf import ensure_csrf_cookie
@ensure_csrf_cookie
def token_security(request):
return HttpResponse() # json or whatever
Затем в любое время, когда вы захотите выполнить POST для конечной точки, защищенной CSRF, и в файлах cookie нет маркера CSRF, выполните GET для этого представления, и он должен установить cookie, который затем можно будет использовать для POST.
Оригинальный ответ ниже:
В моих тестах работает следующее (я использую фабрики для создания пользовательских объектов, но вы можете создать их вручную):
class TestLoginApi(APITestCase):
def setUp(self):
self.client = APIClient(enforce_csrf_checks=True)
self.path = reverse("registration:login")
self.user = UserFactory()
def tearDown(self):
self.client.logout()
def _get_token(self, url, data):
resp = self.client.get(url)
data['csrfmiddlewaretoken'] = resp.cookies['csrftoken'].value
return data
def test_login(self):
data = {'username': self.user.username,
'password': PASSWORD}
data = self._get_token(self.path, data)
# This should log us in.
# The client should re-use its cookies, but if we're using the
# `requests` library or something, we'd have to re-use cookies manually.
resp = self.client.post(self.path, data=data)
self.assertEqual(resp.status_code, 200)
etc.
Если все это делается динамически, вы также должны быть уверены, что ваше представление устанавливает cookie на GET, потому что согласно Django документам (см. Предупреждение), оно не будет установлено автоматически, если вы не отправляете обратно из шаблона, который имел {% csrf_token %}
задавать.
Если вам нужно установить его, это будет выглядеть примерно так (в вашем файле DRF views.py):
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import ensure_csrf_cookie
@method_decorator(ensure_csrf_cookie)
def get(self, request, *args, **kwargs):
return SomeJson...
Наконец, для моих представлений Django Rest Framework я должен был убедиться, что POST также были защищены csrf (но это не похоже на проблему, с которой вы столкнулись):
from django.views.decorators.csrf import csrf_protect
@method_decorator(csrf_protect)
def post(self, request, *args, **kwargs):
return SomeJson...