Ответ
JWT-токен хранится на стороне клиента. Выбор места хранения — это компромисс между удобством и безопасностью. Основные варианты и связанные с ними риски:
-
localStorage/sessionStorage- Описание: Хранилище в браузере, доступное через JavaScript.
sessionStorageочищается при закрытии вкладки. - Плюс: Простота реализации.
- Минус: Уязвимость к XSS-атакам (Cross-Site Scripting). Если злоумышленник внедрит свой скрипт на страницу, он сможет прочитать и украсть токен.
// Сохранение токена localStorage.setItem('jwt_token', 'your.jwt.token');
// Получение токена для заголовка Authorization const token = localStorage.getItem('jwt_token');
- Описание: Хранилище в браузере, доступное через JavaScript.
-
HttpOnlyCookie- Описание: Cookie, которые автоматически отправляются с каждым HTTP-запросом на тот же домен, но недоступны для чтения из JavaScript на клиенте.
- Плюс: Встроенная защита от XSS-атак, так как скрипты не могут прочитать токен.
- Минус: Уязвимость к CSRF-атакам (Cross-Site Request Forgery). Требует дополнительных мер защиты (атрибут
SameSite=StrictилиLax, CSRF-токены).# Пример установки cookie в ответе (Flask) response.set_cookie( 'access_token', 'your.jwt.token', httponly=True, # Запрещает доступ из JS secure=True, # Отправка только по HTTPS samesite='Strict' # Защита от CSRF )
Рекомендуемый подход (Token-Based Authentication)
Наиболее безопасной считается комбинация из двух токенов:
- Access Token (короткоживущий): Хранится в памяти приложения (например, в переменной JavaScript). Используется для авторизации запросов. Его короткий срок жизни (5-15 минут) минимизирует ущерб в случае кражи.
- Refresh Token (долгоживущий): Хранится в
HttpOnlycookie. Используется исключительно для получения новогоaccess token, когда старый истекает.
Этот подход сочетает защиту от XSS (токен не в localStorage) и снижает риски CSRF (критичные операции требуют access token, который не отправляется автоматически как cookie).
Ответ 18+ 🔞
Вот, смотри, разберём эту всю хрень с хранением токенов, а то народ как обезьяны с гранатами — берут куда попало и потом удивляются, что всё взорвалось.
Ну, JWT-токен — это типа твой пропуск в клуб. Выдал тебе его швейцар (сервер), а ты теперь думаешь, куда его сунуть, чтобы и не потерять, и чтобы тебя не обнесли по карманам.
Вариант первый — запихнуть в localStorage или sessionStorage.
Это как оставить ключи от квартиры на видном месте в прихожей.
Плюс: дохуя просто, открыл шкафчик (localStorage.setItem) — положил, достал — пошёл.
Минус: любой скрипт, который проберётся на твою страницу (это и есть XSS-атака), спокойно выковыряет твои ключи и сделает дубликат. Пипец твоей квартире.
// Вот так ты радостно его туда пихаешь
localStorage.setItem('jwt_token', 'your.jwt.token');
// А вот так какой-нибудь скрипт-гадёнок его вытащит
const stolenToken = localStorage.getItem('jwt_token');
Вариант второй — HttpOnly куки.
Это уже умнее. Токен лежит в специальной банке, которую браузер сам приносит серверу с каждым запросом, но твои скрипты туда залезть не могут.
Плюс: от XSS защита на уровне, потому что JS его не прочитает.
Минус: появляется другая засада — CSRF. Если ты зайдёшь на левый сайт, а он тебе подсунет форму, которая стукнет на твой же банковский сайт, браузер автоматом прицепит эту куку, и деньги могут уплыть. Но от этого есть противоядие — флаги Secure, SameSite.
# Сервер тебе такую куку в ответ суёт
response.set_cookie(
'access_token',
'your.jwt.token',
httponly=True, # Руки прочь, JavaScript!
secure=True, # Только по HTTPS, никакого говнотрафика
samesite='Strict' # Не цепляйся к запросам с левых сайтов
)
А теперь, блядь, рекомендуемый подход, чтобы спать спокойно.
Используй два токена, как в хорошем банке: один — для повседневных трат, второй — для возобновления первого, если тот сдох.
-
Access Token (короткоживущий, 5-15 минут).
Храни его в памяти приложения (просто в переменной JS). Он как наличка в кармане — украли, потратили, но ненадолго. Каждый запрос к API цепляешь его в заголовокAuthorization. Украсть его сложнее, а если и украдут — он быстро протухнет. -
Refresh Token (долгоживущий, дни/недели).
Вот его-то и пихай вHttpOnlyкуку. Он нужен только для одной операции — прийти к серверу и сказать: «Э, дружок, access token мой сдох, выдай новый». Поскольку из JS его не вытащить, то и скомпрометировать его овердохуища сложно.
Получается такая схема: XSS-атака не сожрёт refresh token (он в куке), а CSRF-атака не сделает ничего серьёзного, потому что для важных операций нужен access token из памяти, который кукой не отправляется. Красота, ёпта!
Вот так и живём: не кладём все яйца в одну корзину, а то охуеем потом, когда она провалится.