DRF set_cookie не работает, когда frontend находится на localhost, а backend - на удаленном сервере

Я создал приложение DRF, которое использует аутентификацию jwt с httpolnly cookies для аутентификации, а также использует enforce_csrf для предотвращения атак csrf.

from rest_framework_simplejwt.authentication import JWTAuthentication
from django.conf import settings

from rest_framework.authentication import CSRFCheck
from rest_framework import exceptions

def enforce_csrf(request):
    check = CSRFCheck()
    check.process_request(request)
    reason = check.process_view(request, None, (), {})
    print(reason)
    if reason:
         raise exceptions.PermissionDenied('CSRF Failed: %s' % reason)

class CookieBasedJWTAuthentication(JWTAuthentication):
    def authenticate(self, request):
        header = self.get_header(request)
    
        if header is None:
            raw_token = request.COOKIES.get(settings.SIMPLE_JWT['AUTH_COOKIE_ACCESS']) or None
        else:
            raw_token = self.get_raw_token(header)
        if raw_token is None:
            return None
        
        validated_token = self.get_validated_token(raw_token)
        enforce_csrf(request)
        return self.get_user(validated_token), validated_token

Для предотвращения атак csrf django устанавливает cookie, а также 'csrftokenmiddleware' и сравнивает их.

вот пример кода для установки csrf cookie и токена:

class SetCSRFToken(APIView):
permission_classes = [AllowAny]
def get(self, request):
    response = Response() 
    csrf.get_token(request)
    response.status_code = status.HTTP_200_OK
    csrf_secret = csrf.get_token(request)
    response.set_cookie(
        key = 'csrftoken', 
        value = request.META["CSRF_COOKIE"],
        expires = settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'],
        path= settings.SIMPLE_JWT['AUTH_COOKIE_PATH'],
        secure = settings.SIMPLE_JWT['AUTH_COOKIE_SECURE'],
        httponly = False,
        samesite = 'Lax'
    )
    response.data = {"status": "CSRF cookie set", 'csrfmiddlewaretoken':request.META["CSRF_COOKIE"]}

    return response

я также установил :

CORS_ALLOW_ALL_ORIGINS=  True
CORS_ALLOW_CREDENTIALS = True

код работает идеально, когда и frontend и backend находятся на localhost, но когда я запускаю backend на удаленном сервере, cookies не устанавливаются, но браузер получает ответ на запрос.

когда фронтенд находится на том же хосте

когда backend на удаленном сервере, а frontend на localhsot

вот результат консоли для обоих случаев

Нашли! Сначала убедитесь, что вы все правильно поняли в этом ответе: https://stackoverflow.com/a/46412839/18327111

Поскольку django corsheaders middleware проверяет следующие параметры, убедитесь, что у вас есть следующие настройки:

if conf.CORS_ALLOW_ALL_ORIGINS and not conf.CORS_ALLOW_CREDENTIALS:
    response[ACCESS_CONTROL_ALLOW_ORIGIN] = "*"
else:
    response[ACCESS_CONTROL_ALLOW_ORIGIN] = origin

новые настройки:

from corsheaders.defaults import default_headers
CORS_ALLOW_ALL_ORIGINS=  False
CORS_ALLOW_CREDENTIALS = True
CORS_ALLOWED_ORIGINS = [
    "http://yourip_or_domain:3000",
    "http://127.0.0.1:3000",
    "http://localhost:3000",
]
#making sure CORS_ALLOW_HEADERS  is not "*"
CORS_ALLOW_HEADERS = list(default_headers) + ['Set-Cookie']

В случае, если вы не используете аутентификацию сессии django (как я) и хотите обойти ее, добавьте следующие настройки

CSRF_USE_SESSIONS = False
SESSION_COOKIE_SECURE = False
CSRF_COOKIE_SECURE = False
CSRF_COOKIE_SAMESITE = None
SESSION_COOKIE_SAMESITE = None

и основная причина неудачи: это скопировано из chrome developer tools и mozila также имеют это предупреждение:

Поскольку атрибут SameSite файла cookie не был установлен или является недействительным, он по умолчанию принимает значение SameSite=Lax, что предотвращает отправку куки при межсайтовом запросе. Такое поведение защищает данные пользователя от случайной утечки третьим лицам и подделки межсайтовых запросов. Решите эту проблему, обновив атрибуты куки:

Укажите SameSite=None и Secure, если cookie следует отправлять в межсайтовых запросах. Это позволяет использовать их третьими лицами.

Укажите

SameSite=Strict или SameSite=Lax, если cookie не следует отправлять в межсайтовых запросах. отправлять в межсайтовых запросах.

Итак, если вы хотите получить кросс-доступ, единственным способом является использование https и SameSite=None, иначе вам придется развернуть ваш api и бэкенд на одном домене

Обратите внимание, что http и https считаются разными доменами и являются кросс-оригинальными https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite#samesitenone_requires_secure

Куки из одного и того же домена больше не рассматриваются как с одного и того же сайта, если они отправлены по другой схеме (http: или https:).

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