Проблемы с 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

Вернуться на верх