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:).