Куда сохранять токен после получения
Что проверяют
Понимаете ли вы, от чего защищает каждый вариант хранения, а от чего нет. Готового «правильного места» не существует, есть компромисс между XSS и CSRF. Следом спрашивают, как вы будете обновлять токен и что сделает атакующий, если XSS всё-таки случился.
Как отвечать
Разберите варианты:
localStorage: живёт между сессиями и доступен любому скрипту на странице. Одна XSS или скомпрометированная зависимость, и токен уезжает к атакующему;sessionStorage: живёт до закрытия вкладки и не делится между вкладками, но от XSS защищает так же слабо;- память: переменная, замыкание или стор. Не переживает перезагрузку, зато токен не лежит в хранилище, которое можно прочитать целиком;
- cookie с
HttpOnly: скрипт её не видит, браузер сам отправляет её с запросами. НужныSecureиSameSite, а раз кука уходит автоматически, появляется риск CSRF.
Типичная схема для SPA: короткоживущий access-токен на несколько минут в памяти, refresh-токен в cookie HttpOnly; Secure; SameSite=Strict с Path, ограниченным эндпоинтом обновления. При загрузке страницы приложение вызывает refresh и получает новый access. Refresh-токены ротируются: каждый используется один раз, а повторное предъявление старого считается кражей и отзывает всю цепочку.
Надёжнее паттерн BFF: токены остаются на вашем сервере, браузер получает только сессионную куку.
Учтите сетевые детали: для кук на другой origin нужны credentials: 'include' в fetch и Access-Control-Allow-Credentials: true с конкретным origin вместо *. Сторонние куки браузеры режут или изолируют, поэтому API лучше держать на том же сайте, например на поддомене.
Где спотыкаются
Говорят, что HttpOnly решает проблему XSS. Украсть куку скрипт не сможет, но пока страница открыта, он отправит запросы от имени пользователя. Нужны ещё CSP и экранирование вывода.
Переносят токен в куку и забывают про CSRF. SameSite=Lax закрывает большую часть случаев, но не спасает GET-запросы с побочными эффектами и атаки с поддомена того же сайта.
Кладут долгоживущий refresh-токен в localStorage. Украденный, он даёт доступ на недели, а выход из аккаунта на клиенте его не отзывает.