JWT vs сессия, refresh, HS256/RS256, где хранить

О лекции

Сессия держит состояние на сервере: отозвать её проще — удалили ключ в store. JWT access самодостаточен после подписи; отзыв сложнее. HS256 делит секрет с каждым, кто проверяет. RS256 оставляет подпись у issuer. Где хранить токен — развилка XSS против CSRF.

Авторизация и устойчивость HTTP http собеседование

Классическая сессия: cookie с непрозрачным id. Сервер (или Redis) хранит id → пользователь. Горизонталь требует общего store. Отзыв простой: удалили ключ — следующий запрос неаутентифицирован. Цена — lookup на каждый запрос.

JWT access: заголовок, payload (claims), подпись. Resource server может проверить подпись и exp без похода в БД. Удобно между сервисами. Минус: до истечения TTL токен жив, даже если пользователя уже разлогинили. Нужны короткий TTL, blocklist, версия в claim или интроспекция.

OAuth2 — делегирование доступа. Клиент получает access (и часто refresh) у authorization server. Resource server проверяет access. Это не синоним «самописный логин, который кладёт JWT в localStorage». OpenID Connect добавляет идентичность поверх OAuth.

HS256 и RS256

HS256 — HMAC с общим секретом. Любой, у кого есть секрет, может и проверить, и подписать. Если десять сервисов «просто проверяют» одним ключом, компрометация любого из них — фабрика токенов.

RS256/ES256: приватный ключ у issuer, публичный у всех проверяющих. Resource server не умеет ковать токены.

Пример. Сервис отзывов хранит тот же HMAC-секрет, что и auth. Его взломали. Злоумышленник подписывает access с sub=admin. Все остальные сервисы принимают токен: секрет тот же. С RS256 у сервиса отзывов только публичный ключ. Подделать токен он не может.

// JWT: header.payload.signature
// HS256: HMAC(secret, data) — secret есть у всех «проверяющих»
// RS256: sign(private_issuer, data); verify(public, …)
//
// сессия: DELETE session:abc  → отзыв сразу
// JWT access TTL 5m: отзыв = blocklist / ver claim / подождать exp
//
// localStorage: любой XSS читает токен
// Set-Cookie HttpOnly; Secure; SameSite=Lax → JS не видит, нужен CSRF

Access короткий, refresh длиннее. HS256: секрет умеет подписывать. httpOnly cookie не виден JS, но нужен CSRF-контроль.

Refresh и хранение

Access живёт минуты. Refresh — дольше и хранится строже: httpOnly cookie, ротация при каждом использовании. Refresh в localStorage рядом с access — длинный ключ для XSS. Не кладите пароль или персональные данные в payload «без проверки подписи».

XSS против CSRF

Токен в localStorage удобен SPA: JS сам ставит Authorization. Любой XSS крадёт его. Cookie с HttpOnly JS не читает — XSS не вытащит токен простым скриптом. Но браузер приложит cookie к запросу: нужна защита CSRF (SameSite, anti-CSRF token). Это главная развилка, не «cookie всегда неуязвим».

Типичная ошибка

Сказать «JWT безопаснее сессии». Безопасность зависит от TTL, алгоритма, места хранения и отзыва. HS256 «для всех микросервисов одним секретом» превращает каждый сервис в issuer.

На собеседовании

Сессию отозвать проще: ключ из store. Access короткий, refresh длинный, между сервисами — асимметрия (RS256). XSS крадёт JS-хранилище; httpOnly cookie требует CSRF-контроль. OAuth — про делегирование, не про «просто JWT».