Ответ
JWT-токен всегда хранится на стороне клиента. Существует три основных подхода к его хранению, каждый со своими компромиссами между удобством и безопасностью.
-
LocalStorage/SessionStorage- Как работает: Токен сохраняется в хранилище браузера и доступен через JavaScript.
- Плюсы: Простота реализации. Легко получить доступ к токену для отправки в заголовках
Authorization. - Минусы: Уязвимость к XSS-атакам (Cross-Site Scripting). Если злоумышленник сможет внедрить вредоносный JS-код на ваш сайт, он сможет украсть токен из
LocalStorage.
-
HttpOnlyCookie- Как работает: Сервер устанавливает токен в cookie с флагом
HttpOnly. Браузер автоматически отправляет cookie с каждым запросом на тот же домен. - Плюсы: Защита от XSS. Флаг
HttpOnlyзапрещает доступ к cookie из JavaScript, что делает кражу токена через XSS невозможной. - Минусы: Уязвимость к CSRF-атакам (Cross-Site Request Forgery). Поскольку браузер отправляет cookie автоматически, злоумышленник может заставить пользователя выполнить нежелательное действие на вашем сайте, если тот перейдет по вредоносной ссылке с другого сайта. Требуются дополнительные меры защиты (например,
SameSiteатрибут или CSRF-токены).
- Как работает: Сервер устанавливает токен в cookie с флагом
Рекомендации и лучшие практики:
Самым безопасным подходом на сегодняшний день считается использование HttpOnly cookie в связке с дополнительными мерами защиты:
- Флаг
Secure: Cookie будет отправляться только по HTTPS. - Атрибут
SameSite:SameSite=Strict: Cookie не будет отправлен ни с какими межсайтовыми запросами. Максимальная защита от CSRF.SameSite=Lax: Cookie отправляется при навигации на ваш сайт (например, по ссылке), но не при запросах от других сайтов (AJAX,<img>). Хороший баланс безопасности и удобства.
- Короткое время жизни Access-токена: Используйте короткоживущие access-токены (5-15 минут) и долгоживущие refresh-токены для их обновления. Refresh-токен также должен храниться в
HttpOnlycookie.
Ответ 18+ 🔞
Так, слушай, про эти ваши JWT-токены, блядь. Ситуация, как с выбором, где спрятать последнюю бутылку: куда ни сунь — везде риск, что сопрут или сам разольёшь, ёпта.
Вот смотри, есть три классических способа, куда эту писюльку-токен воткнуть. И каждый — какой-то компромисс между «ой, как удобно» и «бля, сейчас всё взломают».
1. LocalStorage / SessionStorage (или «Просто положи на видное место»)
- Суть: Засовываешь токен прямиком в карман браузера. Достаёшь его своим JS-кодом, когда надо, и суёшь в заголовок запроса.
- Плюс: Охуенно просто. Написал
localStorage.setItem('token', 'тут_твой_хлам')— и поехали. - Минус: А минус-то, блядь, жирный! Это же полная дыра для XSS. Если какая-то мартышлюшка встроит на твою страничку зловредный скриптик — он вытащит твой токен из этого хранилища, как конфетку из кармана ребёнка. И всё, пиши пропало. Твоя авторизация теперь у того пидораса шерстяного.
2. HttpOnly Cookie (или «Спрячь поглубже, но не забывай про другую дверь»)
- Суть: Сервер, как хитрая жопа, присылает токен в куках и ставит волшебный флажок
HttpOnly. Браузер берёт эту куку, кладёт в свой сейф, и оттуда её JS-код уже не достать. Зато браузер сам, автоматом, будет прикладывать её к каждому запросу на твой домен. - Плюс: Защита от XSS — есть! Тот самый скриптик упрётся лбом в этот флажок и нихуя не украдёт. Красота.
- Минус: Но расслабляться рано, ёпта! Появляется другая беда — CSRF. Поскольку браузер автоматом лепит куку куда надо, злоумышленник может на своём сайте сделать такую форму или ссылку, что твой юзер, сам того не ведая, отправит запрос к тебе уже с этой авторизационной кукой. И сделает что-нибудь эдакое, типа «переведи все деньги на счёт 123». Короче, подстава.
Так что же делать, блядь? Куда бежать?
Сейчас все адекватные дядьки склоняются к варианту HttpOnly Cookie, потому что от XSS защищает на ура. Но чтобы не получить по ебалу от CSRF, надо эту куку дополнительно обвешать защитой, как новогоднюю ёлку:
- Флаг
Secure: Чтоб эта кука путешествовала только по защищённому HTTPS. Без этого — вообще никуда. - Атрибут
SameSite: Вот это важная хуйня!SameSite=Strict: Самый строгий режим. Кука не отправится ни с какими запросами с других сайтов. CSRF? Да похуй! Но и удобство немного страдает.SameSite=Lax: Золотая середина. Кука отправится, если юзер просто перешёл по ссылке на твой сайт (логично же), но не отправится, если запрос идёт с чужого сайта через<img>,<script>или fetch. Баланс, блядь, между паранойей и жизнью.
- Короткоживущие токены: Не делай один токен на вечность. Пусть access-токен живёт 10-15 минут, как майский жук. А для его обновления используй отдельный, долгоживущий refresh-токен. И этот refresh-токен — да, ты угадал — тоже храни в
HttpOnlyкуке! Чтобы, даже если access-токен скомпрометируют, долго радоваться не пришлось.
Вот так вот, коротко и без соплей. Выбирай, что важнее: простота, которая аукнется, или безопасность, которую надо ещё и правильно настроить, блядь.