Как работают сессии в браузере
Что проверяют
Понимаете ли вы, где на самом деле живёт сессия, что хранит браузер и как это влияет на код фронтенда: запросы с куками, CORS, выход из аккаунта. Следом часто спрашивают, чем серверная сессия отличается от JWT и где хранить токен.
Как отвечать
Под словом «сессия» в браузере прячутся три разные вещи, и сильный ответ их разводит:
- серверная сессия: после логина сервер создаёт запись с данными пользователя, а браузеру отдаёт только идентификатор:
Set-Cookie: sid=...; HttpOnly; Secure; SameSite=Lax. Браузер прикладывает куку к каждому запросу, сервер по ней находит запись в Redis или базе. Идентификатор берут из криптостойкого генератора и меняют при входе; - сессионная кука: кука без
ExpiresиMax-Age, которая по стандарту живёт до закрытия браузера; sessionStorage: хранилище на одну вкладку и один origin. Переживает перезагрузку, исчезает с закрытием вкладки, другие вкладки его не видят.
Для фронтенда отсюда следуют конкретные вещи. Куку с HttpOnly скрипт не прочитает, поэтому узнать, залогинен ли пользователь, можно только запросом вроде /api/me. Если API на другом origin, fetch по умолчанию куки не отправит: нужен credentials: 'include', а сервер должен ответить Access-Control-Allow-Credentials: true и конкретным origin вместо *. Выход требует запроса на сервер, чтобы уничтожить запись, стереть что-то на клиенте мало. Разлогинить остальные вкладки помогает BroadcastChannel или событие storage.
Альтернатива это JWT без состояния на сервере: проще масштабировать, но отозвать выданный токен до истечения срока без чёрного списка нельзя.
Где спотыкаются
Кладут токен доступа в localStorage. Любой XSS прочитает его одной строкой, а HttpOnly-кука хотя бы не утечёт в чужие руки целиком.
Считают, что сессионная кука гарантированно исчезает при закрытии браузера. Chrome и Firefox с включённым восстановлением вкладок восстанавливают и её.
Думают, что sessionStorage общий для всех вкладок сайта. Он изолирован по вкладке, а при дублировании вкладки копируется, и дальше две копии живут независимо.