HTTP-ответ Django всегда устанавливает куки `sessionid`, и данные сессии не сохраняются
Я создал пользовательский бэкенд и связанное с ним промежуточное ПО, которое регистрирует пользователей при единственном условии, что вместе с запросом передается cookie ID_TOKEN (аутентификация осуществляется с помощью AWS Cognito + Lambda Edge, управляемой AWS CouldFront).
Мой код в значительной степени основан на django.contrib.auth.backends.RemoteUserBackend
и связанном с ним промежуточном программном обеспечении middleware django.contrib.auth.middleware.RemoteUserMiddleware
.
В то время как работа с пользовательскими данными сессии работает нормально как локально, так и в контейнере Docker с помощью runserver + юнит-тесты проходят, в продакшене (код, запущенный в контейнере на AWS ECS) я теряю все данные сессии от одного запроса/ответа к другому.
Из того, что я могу видеть в сетевой вкладке Firefox, заголовок set-cookie
всегда отправляется с HTTP-ответом, что приводит к потере данных сессии. Я предполагаю, что они должны быть смыты также на стороне бэкэнда (сессии используют хранилище базы данных, производство работает на gunicorn).
Я установил SESSION_COOKIE_SECURE = True
в продакшене, но это не решило проблему.
Более того, использование django_extensions
и его runserver_plus
с автоматически сгенерированным сертификатом для локального использования HTTPS также не позволило мне воспроизвести проблему.
Вот один set-cookie
пример:
set-cookie sessionid=rlc...tn; expires=Mon, 03 Feb 2025 14:29:53 GMT; HttpOnly; Max-Age=1209600; Path=/; SameSite=Lax; Secure
Кто-нибудь уже сталкивался с подобной проблемой?
Сессия смывается из-за следующих строк в вашем промежуточном ПО:
if request.user.is_authenticated: if request.user.get_username() == token.username and not has_id_token_digest_changed: return # username and token did not change, do nothing self._remove_invalid_user(request)
Вызов self._remove_invalid_user(request)
является проблемой, так как при этом происходит выход пользователя из системы, что приводит к стиранию сессии в целях безопасности. Поскольку вы упомянули, что разработали это после обращения к RemoteUserMiddleware
, соответствующие строки там выглядят следующим образом:
if request.user.is_authenticated: if request.user.get_username() == self.clean_username(username, request): return self.get_response(request) else: # An authenticated user is associated with the request, but # it does not match the authorized user in the header. self._remove_invalid_user(request)
Обратите внимание, что в этом случае _remove_invalid_user
вызывается только в том случае, если имя пользователя удаленного пользователя не совпало с именем пользователя вошедшего в систему. В вашем случае вы вызываете метод, даже если имя пользователя совпало, но ID-токен не совпал.
Вы, похоже, пытаетесь проверить, изменился ли ID-токен или нет, проверяя его хэш, но это не имеет особого смысла, учитывая, что ID-токен - это JWT, который обычно не имеет длительного срока действия. Вполне возможно, что измененный токен предназначен для той же личности пользователя и является просто заменой токена с истекшим сроком действия. Если вы хотите подтвердить, что токен принадлежит тому же пользователю, вам следует использовать какое-либо идентифицирующее утверждение из токена, например утверждение sub
и т. д. Пока предположим, что ваше имя пользователя идентифицирует его однозначно, вы можете изменить эти строки следующим образом:
if request.user.is_authenticated:
if request.user.get_username() == token.username:
return # username did not change, do nothing
else:
self._remove_invalid_user(request)
Благодаря вам, Абдул Азиз Баркат, я смог свести проблему к слишком строгому белому списку файлов cookie в AWS CloudFront. Спасибо!
Добавление обоих стандартных имен куки Django sessionid
и csrftoken
в белый список куки решило мою проблему (сессия сохраняется вместе с данными сессии и проверка CSRF проходит успешно).
Для тех из вас, кто интересуется некоторыми вопросами, связанными с облаком / IaC, помните, что вам нужно правильно настроить политику Cookies в CloudFront. Вот немного документации Terraform об этом.