Где хранится JWT токен на клиенте?

Ответ

При тестировании веб-приложений важно понимать, где клиентское приложение хранит JWT (JSON Web Token), так как это напрямую влияет на векторы атак. Основные места хранения:

1. В памяти (In-Memory / JavaScript переменные)

  • Как: Токен хранится в переменной JavaScript (например, в состоянии SPA-фреймворка типа React/Vue).
  • Безопасность: Высокая. Токен очищается при закрытии вкладки. Уязвим к XSS, но атакующий должен внедрить и выполнить вредоносный JS в контексте страницы.
  • Тестирование: Проверяем, что токен не попадает в менее безопасные хранилища (LocalStorage) и очищается после логаута.

2. В сессионном хранилище (sessionStorage)

  • Как: sessionStorage.setItem('access_token', token)
  • Безопасность: Средняя. Токен сохраняется между перезагрузками страницы, но очищается при закрытии вкладки. Уязвим к XSS — если есть уязвимость, скрипт может прочитать токен из sessionStorage.

3. В локальном хранилище (localStorage)

  • Как: localStorage.setItem('access_token', token)
  • Безопасность: Низкая. Токен сохраняется навсегда, пока его явно не удалят. Крайне уязвим к XSS. Не рекомендуется для хранения токенов доступа (access token).

4. В защищенных куках (HttpOnly, Secure, SameSite Cookies)

  • Как: Сервер устанавливает cookie с флагами HttpOnly, Secure, SameSite=Strict.
  • Безопасность: Высокая против XSS (токен недоступен через JavaScript). Требует защиты от CSRF (с помощью того же флага SameSite или CSRF-токенов).

Современная и безопасная практика, которую я проверяю:

  • Короткоживущий Access Token хранится в памяти JavaScript.
  • Долгоживущий Refresh Token хранится в HttpOnly, Secure, SameSite=Strict cookie. Он используется для получения нового access token'а, не участвуя в каждом запросе к API, что минимизирует риски.

Ответ 18+ 🔞

А, слушай, вот тема интересная, про то, куда эти ваши JWT-токены прячут. Тут, бля, как в жизни — положишь не туда, и всё, пиши пропало, придут гомосеки и налетят. Так что давай разбираться, где что лежит и почему это важно.

Ну, первое место — это в памяти, прямо в JavaScript-переменных. Это типа как держать пачку денег в кармане, а не в сейфе. Закрыл вкладку — и всё, токен испарился, как твоя зарплата в пятницу вечером. С точки зрения безопасности — высокая, потому что злоумышленнику надо сначала внедрить свой скрипт прямо на страницу (это XSS называется), чтобы эту переменную вытащить. Но если уж он смог это сделать, то считай, что ты уже в жопе. При тестировании смотри, чтобы после логаута этот токен реально удалялся, а не болтался где-то, как манда с ушами.

Дальше идёт сессионное хранилище (sessionStorage). Это уже чуть посерьёзнее. Токен переживает перезагрузку страницы, но если вкладку закрыл — всё, хуй с горы. Записывается так: sessionStorage.setItem('access_token', token). Безопасность средняя. Почему? Потому что он всё равно уязвим к тому же XSS. Если нашли дыру, скрипт спокойно прочитает sessionStorage и вытащит твой ключ от царства небесного. Так что доверия к этому способу — ебать ноль, если приложение криво написано.

А вот локальное хранилище (localStorage) — это вообще пиздец, ядрёна вошь. Токен там лежит, пока его вручную не сотрёшь, как старый хлам в гараже. Код тот же: localStorage.setItem('access_token', token). Безопасность низкая, бля буду! Это как оставить ключи от квартиры под ковриком — первый же XSS-скрипт, который проберётся на страницу, вынесет твой токен, и потом какой-нибудь пидарас шерстяной будет от твоего имени заказы делать. Категорически не рекомендую так делать, это уровень "сам от себя охуел".

И наконец, вариант для взрослых — защищённые куки (HttpOnly, Secure, SameSite Cookies). Вот это уже серьёзно. Сервер ставит куку с такими флагами, что до неё из JavaScript вообще не дотянуться. Безопасность высокая, потому что от XSS она защищает на ура. Но тут своя засада — нужна защита от CSRF, чтобы тебя не заставили отправить запрос с этими куками помимо твоей воли. Флаг SameSite или отдельные CSRF-токены решают эту проблему.

Так какая сейчас модная и безопасная практика, которую я как тестировщик должен искать? Вот смотри, чтобы было красиво и правильно:

  1. Короткоживущий Access Token — этот засланец должен жить только в памяти (в JavaScript). Он как одноразовая зажигалка — потух и забыл.
  2. Долгоживущий Refresh Token — а вот эту штуку нужно запихивать в ту самую HttpOnly, Secure, SameSite=Strict cookie. Он не светится в каждом запросе к API, а используется тихо и мирно только для того, чтобы выпросить новый access token, когда старый сдох.

Если видишь такую архитектуру — значит, разработчики не совсем распиздяи, они думали головой. Если же access token валяется в localStorage — это волнение ебать, сразу пиши в отчёт, что вы ходите по охуенно тонкому льду. Всё, вопросов нет?