Проблемы с CSRF на форме входа в систему с React SPA и Django
Я создаю React SPA с бэкендом Django и Oauth с использованием инструментария Django OAuth и был попрошен кем-то из команды безопасности реализовать защиту CSRF на форме входа в приложение.
Нет CSRF cookie, так как Django не обслуживает внешнее приложение и пользователи не будут входить в систему через сессии Django.
Я создал конечную точку API для предоставления CSRF-токенов - в некоторой степени следуя инструкциям в этом руководстве: https://www.stackhawk.com/blog/react-csrf-protection-guide-examples-and-how-to-enable-it/ Приложение React выполняет вызов этой конечной точки для получения CSRF-токена.
Для добавления CSRF-защиты на URL входа - предоставляемой Oauth toolkit /o/token - я подклассифицировал Oauth toolkit TokenView и указал на него мой URL входа. Я использовал декоратор для добавления защиты csrf:
@method_decorator(csrf_protect, name="dispatch")
class GetToken(TokenView):
pass
Это, кажется, работает правильно, но при выполнении запроса на вход я не могу заставить Django принять CSRF-токен, который я получил из конечной точки - я пытаюсь предоставить его в теле запроса (как csrfmiddlewaretoken) и как HTTP-заголовок (X-CSRFToken).
Когда я делаю запрос и отлаживаю промежуточное ПО Django CSRF, я вижу, что запрос отклоняется, потому что POST-запросы требуют токен, отправленный только в виде cookie. Сейчас я раздумываю над тем, стоит ли проверять CSRF-токен вручную в моем подклассифицированном представлении, но мне интересно, не делаю ли я здесь что-то принципиально неправильное, поскольку мой подход кажется все более халтурным. Может ли кто-нибудь предложить лучший метод для добавления защиты CSRF?
Спасибо
Не уверен, но проверяли ли вы, действительно ли cookie устанавливается в браузере? Afaik вы должны получить cookie из конечной точки Django и затем установить его в вашем ajax вызове в заголовке запроса post.
Вы можете убедиться, что cookie отправляется вашей конечной точкой, добавив декоратор "ensure_csrf_cookie" следующим образом:
@method_decorator(ensure_csrf_cookie, name='dispatch')
class GetCSRFToken(APIView):
permission_classes = (permissions.AllowAny,)
def get(self, request, format=None):
return Response({'success': 'CSRF returned'}, status=status.HTTP_200_OK)
Затем получите CSRF cookie с помощью JS следующим образом:
function getCookie(name) {
let cookieValue = null;
if (document.cookie && document.cookie !== '') {
const cookies = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
const cookie = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
const csrftoken = getCookie('csrftoken');
Это описано в документации: https://docs.djangoproject.com/en/3.2/ref/csrf/#ajax