Почему токен лучше сохранять в куки, а не в localStorage или sessionStorage
Что проверяют
Отличаете ли вы «защищено от кражи токена» от «защищено от атаки». Ответ «в куки безопаснее» без оговорок тянет уточнение: а от CSRF что защитит и почему при XSS всё равно плохо.
Как отвечать
Разница в том, кто имеет доступ к хранилищу. localStorage и sessionStorage читает любой скрипт страницы, включая транзитивную зависимость из npm. Куку с HttpOnly JavaScript не видит вовсе, и токен не выгрузить на чужой сервер.
- атрибуты:
HttpOnlyзакрывает доступ из скрипта,Secureразрешает отправку только по HTTPS,SameSite=Lax(по умолчанию в Chrome и Edge) отсекает межсайтовую отправку,Pathсужает область видимости, префикс__Host-запрещает подменить куку с поддомена; - цена куки это CSRF: браузер шлёт её автоматически, поэтому для изменяющих запросов нужны
SameSiteплюс CSRF-токен или проверкаOriginна сервере. УlocalStorageтакой проблемы нет: заголовок ставит сам код; - честная граница:
HttpOnlyне спасает от XSS как такового: внедрённый скрипт всё равно сделает запрос от имени пользователя, просто не унесёт токен наружу. Первый рубеж этоContent-Security-Policyи отказ от вставки чужого HTML, куки лишь снижают ущерб; - рабочая схема: короткоживущий access-токен в памяти вкладки (переменная, не хранилище), refresh-токен в куке
HttpOnly; Secure; SameSite=Laxс узкимPathвроде/auth/refreshи ротацией при обновлении. Либо BFF, который держит сессию у себя и отдаёт браузеру сессионную куку; - межсайтовость: если фронтенд и API на разных доменах верхнего уровня, кука становится сторонней и её режут. Лечится общим доменом (
app.example.comиapi.example.com),credentials: 'include'и корректным CORS, а не переездом вlocalStorage.
Где спотыкаются
Говорят, что HttpOnly защищает от XSS. Он защищает значение от кражи, но не мешает атакующему действовать в уже открытой сессии.
Ставят куку из JavaScript через document.cookie и считают это защитой. Такая кука по определению не HttpOnly, преимущество исчезло.
Забывают про CSRF при переезде из localStorage в куки. Схема стала уязвима к тому, чего раньше не было, и SameSite с проверкой Origin нужны в том же изменении.