Хранение токенов и их обновление

↑ 5.5 Аутентификация и доступ: Keycloak, SSO, ACL · ← Предыдущая · → Следующая

Обязательно~3 мин чтенияУровень: middle

Зачем это на собесе

«Где хранить токен?» — вопрос с компромиссами XSS и CSRF.

Объяснение

МестоXSSCSRFПереживает перезагрузкуКомментарий
Память (переменная/Pinia без persist)сложнее украсть постоянно, но скрипт в странице всё равно может вызвать APIнетнетлучший вариант для access; восстановление через refresh cookie
localStorage / sessionStorageтокен читаем любым скриптомнетда / вкладкараспространено, но рискованно при XSS
HttpOnly cookieне читается JSуязвимость к CSRF — нужны SameSite, CSRF-токеныдапредпочтительно для refresh и сессий
BFF (сервер хранит токены)токенов в браузере нетзащита CSRFда (cookie сессии)наиболее безопасная схема для SPA
IndexedDB, Service Workerкак localStorage—дане лучше

Рекомендуемая схема:

  1. Access token — в памяти.
  2. Refresh token — HttpOnly; Secure; SameSite cookie (или полностью на BFF).
  3. При загрузке страницы — silent refresh через cookie → access в память.
  4. Ротация refresh-токена, отзыв при выходе.
  5. Строгий CSP, экранирование, обновление зависимостей (снижают риск XSS).

Обновление в нескольких вкладках:

const lock = navigator.locks;                                   // Web Locks: одна вкладка обновляет, остальные ждут
export async function refreshOnce() { return lock.request("refresh", async () => { if (tokenValid()) return; await doRefresh(); channel.postMessage("refreshed"); }); }

Выход: очистка стора, кэша Vue Query, IndexedDB, отзыв токена, редирект на logout IdP; синхронизация выхода между вкладками (storage/BroadcastChannel).

Дополнительно: Content Security Policy, Subresource Integrity, Trusted Types, отсутствие токенов в URL/логах/аналитике.

Нюансы и подводные камни

  • «Токен в localStorage» удобен, но любая XSS-уязвимость = кража токена.
  • HttpOnly cookie без CSRF-защиты открывает CSRF.
  • Долгоживущие access-токены увеличивают ущерб.
  • Токены попадают в отчёты об ошибках и логи — фильтруйте (Sentry beforeSend).
  • Логаут только на клиенте не отзывает токен на сервере.

Практика

  1. Реализуйте access в памяти + refresh в cookie с silent refresh.
  2. Добавьте синхронизацию выхода между вкладками.
  3. Настройте фильтрацию токенов в Sentry.

Вопросы с ответами

Связанные темы