Где безопасно хранить токены аутентификации в браузере?

Ответ

Наиболее безопасный способ — использование HttpOnly, Secure, SameSite cookies. Это защищает от атак межсайтового скриптинга (XSS) и межсайтовой подделки запросов (CSRF).

Пример установки защищённой куки на бэкенде (Node.js/Express):

res.cookie('auth_token', signedJwtToken, {
    httpOnly: true,    // Недоступна из JavaScript
    secure: true,      // Передаётся только по HTTPS
    sameSite: 'strict', // Защита от CSRF
    maxAge: 24 * 60 * 60 * 1000 // 1 день
});

Почему не LocalStorage/SessionStorage?

  • Они доступны через JavaScript, что делает токен уязвимым при XSS-атаке.
  • Злоумышленный скрипт может прочитать токен и отправить его на свой сервер.

Современный подход для SPA (Single Page Application):

  1. Access Token: Короткоживущий (минуты), хранится в памяти JavaScript (переменная, замыкание). При обновлении страницы теряется, и приложение запрашивает новый через специальный endpoint.
  2. Refresh Token: Долгоживущий, хранится строго в HttpOnly куке. Используется для получения новой пары access/refresh токенов.

Пример потока:

  1. Пользователь логинится, бэкенд устанавливает refresh_token в HttpOnly куку и возвращает access_token в теле ответа.
  2. SPA сохраняет access_token в памяти и использует для API-запросов.
  3. Когда access_token истекает, SPA отправляет запрос на /refresh (кука refresh_token отправится автоматически).
  4. Бэкенд валидирует refresh_token и выдаёт новую пару.

Этот подход минимизирует риски утечки долгоживущих учётных данных.

Ответ 18+ 🔞

Давай разберём эту тему, а то народ часто путается, как будто на дворе 2002-й год и все хранят токены в localStorage, а потом удивляются, почему их аккаунты уплыли к левым ребятам.

Самый надёжный вариант — это HttpOnly, Secure, SameSite куки. Представь, что это как сейф, который ты отдаёшь браузеру на хранение, а ключ от него только у сервера. JavaScript доступа к нему не имеет вообще — вот и вся защита от XSS. А SameSite не даёт куке улететь с твоим сайта на левые домены, что убивает большинство CSRF-атак на корню.

Вот как это на бэкенде выглядит (Node.js/Express):

res.cookie('auth_token', signedJwtToken, {
    httpOnly: true,    // Недоступна из JavaScript — никакой ерунды!
    secure: true,      // Только по HTTPS, чтоб не светить в открытую
    sameSite: 'strict', // Жёсткая защита от CSRF
    maxAge: 24 * 60 * 60 * 1000 // Живёт сутки
});

А теперь про LocalStorage/SessionStorage. Ребята, это же пиздец, а не хранилище для токенов! Это как оставить ключи от квартиры на видном месте в подъезде. Любой скрипт, который просочится на твою страницу (а такое бывает овердохуища часто), спокойно вычитает твой токен и отправит его куда надо. Доверия к этому подходу — ноль ебать.

Современный, умный подход для SPA (того самого одностраничного приложения):

  1. Access Token (Токен доступа): Живёт недолго, минут 15-30. Хранится только в памяти JavaScript — в переменной, в замыкании. Обновил страницу — токен испарился, и всё. Это как пропуск на день, который сгорает вечером.
  2. Refresh Token (Токен обновления): Вот это уже серьёзная вещь. Долгоживущий, хранится строго в той самой HttpOnly куке, которую мы описали выше. Его задача одна — выпросить у сервера новую пару токенов, когда access'у пришёл пиздец.

Как это работает на практике, чтоб было понятно:

  1. Юзер логинится. Бэкенд в ответе ставит refresh_token в защищённую куку, а в теле JSON'а отдаёт access_token.
  2. Твое SPA хватает этот access_token, пихает его в память и с ним ходит по всем API.
  3. Внезапно время вышло, токен протух. Вместо того чтобы посылать юзера на повторный логин, приложение тихонько стучится на специальный эндпоинт /refresh. Браузер автоматом цепляет к запросу ту самую HttpOnly куку с refresh_token — тебе даже палец о палец ударить не надо.
  4. Бэкенд проверяет refresh-токен, и если всё чисто — выдаёт свежую парочку: новый access (в JSON) и новый refresh (в куке).

Вот такая хитрая жопа. Риск утечки долгоживущих данных сводится почти к нулю, потому что самый ценный refresh-токен JavaScript'у не виден вообще. А короткоживущий access-токен, даже если его и скомпрометируют, быстро сдохнет. Красота, ёпта.