ООО «Ивент Хаб» · Москва · работаем по всей России
Пн—Пт, 10:00—19:00hello@gusi-lebedi.comРеквизиты
Практическое руководство

JWT: как работает токен и что проверять перед доступом к данным

Как сервер узнаёт пользователя после входа и почему действительная подпись ещё не разрешает открыть чужой заказ. Разбираем структуру, проверку и жизненный цикл JWT.

JWT и вопросы проверки назначения, срока и подписи

Пользователь вошёл в личный кабинет и открыл свой заказ. Через минуту он обновил страницу. Откуда сервер знает, что это тот же человек, если пароль при каждом запросе заново не вводят? Один из вариантов состоит в том, чтобы после входа выдать приложению токен, а затем проверять его при обращении за данными.

В Гуси-Лебеди мы предлагаем разбирать JWT именно на таких действиях. За длинной строкой должны стоять понятные правила: кто выдал её, какое приложение вправе принять, когда она перестаёт действовать и какие операции разрешены пользователю.

JWT, JSON Web Token, представляет собой формат передачи утверждений, или claims. Например, утверждение может указывать пользователя, издателя и получателя токена. Формат описан в RFC 7519. Его применяют в системах аутентификации и доступа, но сам по себе он не реализует вход, выход или проверку прав на заказ.

Представим условный интернет-магазин. После проверки учётных данных сервис входа выдаёт токен. Браузер передаёт его вместе с запросом к API заказов. API проверяет токен по своим правилам, определяет пользователя и отдельно решает, можно ли показать именно этот заказ. Это пример устройства системы, а не описание выполненного нами проекта.

Почему не ограничиться обычной сессией? При сессионном подходе клиент может передавать случайный идентификатор, а состояние сессии хранится на сервере. JWT позволяет передать проверяемые утверждения внутри токена. Это бывает полезно при обмене между сервисами, однако для отзыва доступа всё равно может понадобиться серверное состояние. Переход на JWT не даёт автоматического выигрыша в скорости или безопасности: сначала нужно понять задачу приложения.

Как читать структуру JWT и не путать подпись с шифрованием

Заголовок, содержимое и подпись компактного подписанного JWT

Знакомая строка из трёх фрагментов через точку относится к компактному подписанному JWT, оформленному как JWS. Её можно представить так: header.payload.signature. Заголовок сообщает параметры защиты, payload содержит утверждения, последняя часть позволяет проверить целостность и подлинность с соответствующим ключом.

Заголовок и payload в таком токене кодируются в Base64url. Это способ представить данные строкой, а не спрятать их. Человек, получивший токен, может прочитать содержимое этих частей без секретного ключа. Поэтому найденное в декодере имя пользователя не означает ни взлома подписи, ни успешной проверки доступа.

Подпись защищает от незаметного изменения данных, но не скрывает их. Если заменить идентификатор пользователя в payload, проверка исходной подписи должна завершиться отказом. Простое декодирование этого не обнаруживает: оно только показывает прочитанные данные.

У JWT есть и зашифрованная форма, JWE. У неё другой формат, а для чтения защищённого содержимого нужна соответствующая криптографическая операция. Поэтому обе фразы «любой JWT состоит из трёх частей» и «JWT никогда не шифруется» неточны. В этой статье основной пример относится к подписанному JWS.

В payload обычно обсуждают несколько стандартных полей:

  • iss: кто выпустил токен. Получатель должен знать, каким издателям доверяет.
  • sub: о каком субъекте говорится. В примере магазина это может быть идентификатор покупателя.
  • aud: кому предназначен токен. Токен одного приложения не следует автоматически принимать в другом.
  • exp: момент истечения срока. Это время, а не длительность в минутах.
  • nbf: момент, раньше которого токен нельзя принимать.
  • iat: время выпуска. Само наличие этого поля не задаёт срок действия.
  • jti: идентификатор токена. Он может участвовать в дополнительных механизмах контроля, но сам по себе ничего не отзывает.

RFC не делает все эти поля обязательными для каждого возможного применения. Обязательный набор и правила проверки задаются профилем конкретной системы. Фраза «в токене есть exp» ещё не подтверждает, что библиотека требует это поле и отклоняет просроченное значение.

Для нашего магазина достаточно обсуждать необходимые идентификаторы и служебные условия. Пароль, платёжные сведения и подробности заказа не нужно помещать в открыто читаемую нагрузку. Чем меньше таких данных передаётся, тем меньше придётся раскрыть при утечке токена.

Если хочется посмотреть структуру, используйте вымышленные данные. Рабочий токен не стоит вставлять в случайный онлайн-декодер: в системе с Bearer-доступом тот, кто получил действующий токен, может воспользоваться им. Прочитать учебный образец и отправить постороннему свой пропуск в личный кабинет являются разными действиями.

Что проверять перед принятием токена

Проверка назначения и защиты токена перед принятием

Декодирование, проверка токена и разрешение операции идут друг за другом, но решают разные задачи. Decode позволяет прочитать строку. Verify проверяет её защиту и условия приёма. Authorize отвечает на вопрос о конкретном действии. Смешивать эти этапы опасно даже тогда, когда пример входа работает без ошибок.

В магазине покупатель запрашивает заказ. Сначала API должен получить ожидаемый токен и проверить его. Затем он сопоставляет пользователя с заказом и правилами доступа. Даже действительный токен покупателя не должен разрешать просмотр чужих заказов только потому, что в адресе указан другой номер.

Правила приёма задаёт сервер, а не сама пришедшая строка. В RFC 8725 отдельно рассматриваются допустимые алгоритмы, проверка криптографических операций, издателя и получателя, а также различение токенов разных назначений. Из заголовка можно узнать заявленный алгоритм, но нельзя брать оттуда произвольный список разрешённых алгоритмов.

Например, при использовании HS256 выпуск и проверка используют общий секрет. При RS256 подписывает владелец закрытого ключа, а проверять можно открытым ключом. Это разные модели управления ключами. Нельзя незаметно переключать их по желанию отправителя или обращаться с открытым ключом как с общим секретом.

Перед разработкой зафиксируйте правила приёма:

  • Кто выпускает токен и как API получает доверенные ключи.
  • Какие алгоритмы разрешены, какой получатель ожидается и какие поля обязательны.
  • Какие условия времени проверяются и в каких случаях доступ отклоняется.

Не нужно писать криптографическую проверку вручную ради короткого примера: библиотеку выбирают вместе с её настройками и документацией.

Отдельно проверьте источник ключей. Полученный из токена адрес не становится доверенным только потому, что его назвали адресом набора ключей. Приложение должно получать ключи из согласованного источника и корректно обрабатывать неизвестный ключ или смену ключей издателя.

Токены доступа, обновления и подтверждения личности нельзя считать взаимозаменяемыми. JWT является форматом; access token описывает назначение токена доступа, а refresh token служит для получения нового доступа. Не каждый такой токен обязан быть JWT. Слово Bearer в заголовке описывает способ предъявления, а не структуру строки. Если задача состоит в предоставлении приложению доступа к другому сервису, отдельно разберите разрешения и обмен в OAuth 2.0: формат токена не заменяет этот процесс.

При проектировании серверной части эти условия стоит обсуждать вместе с правами пользователей. Требование «сделать авторизацию через JWT» нужно дополнить поведением: кто может читать заказ, кто изменяет его и что произойдёт после блокировки учётной записи.

Как согласовать срок, хранение и завершение доступа

Срок, хранение и завершение доступа с токеном

Токен выдан, но работа с доступом на этом не заканчивается. Покупатель может закрыть вкладку, вернуться позже, выйти из кабинета или потерять права. Для каждого случая нужны согласованные действия сервера и интерфейса. Если профиль требует exp, сервер проверяет его при приёме токена. Формат времени и небольшой допустимый сдвиг часов должны быть понятны реализации. Не путайте секунды с миллисекундами и не заменяйте проверку на сервере таймером в браузере. Длительность выбирают по требованиям системы; универсальное значение для всех сайтов мы здесь не назначаем.

Обновление. Access token можно заменить новым через предусмотренный механизм. Refresh token относится к этой операции, а не к чтению заказа. Нужно определить его срок, хранение, отзыв и обработку повторного использования. Ротация означает выдачу нового токена обновления и прекращение действия предыдущего по правилам системы. Одно слово refresh в документации ещё не подтверждает наличие такого контроля.

Выход и изменение прав. Удаление токена из браузера прекращает его отправку этим браузером, но не обязательно делает другую копию недействительной. Если нужен досрочный отзыв, его надо реализовать на сервере: например, через проверяемое состояние сессии или список отозванных идентификаторов. Следует также решить, когда начинает действовать новая роль пользователя. Роль, записанная в ранее выданном токене, может оказаться устаревшей.

Хранение зависит от архитектуры, однако удобство не заменяет защиту. localStorage и sessionStorage доступны JavaScript того же origin. При XSS вредоносный код может прочитать находящийся там токен. В актуальном руководстве OWASP по сессиям рекомендуется не хранить в этих API токены аутентификации.

Cookie с HttpOnly нельзя прочитать через обычный JavaScript страницы, а Secure ограничивает передачу HTTPS. Но HttpOnly не устраняет XSS целиком: внедрённый код всё ещё может инициировать действия от лица пользователя. Автоматическая отправка cookie также требует защиты от CSRF. SameSite помогает ограничивать передачу между сайтами, но его не следует считать заменой всех остальных проверок.

Для отдельного фронтенда и API согласуйте домены, область cookie и правила запросов между ними. Для схемы Bearer клиент может передавать токен в заголовке Authorization. Для схемы с HttpOnly cookie браузер передаёт cookie по её правилам; читать её в JavaScript, чтобы затем вставить в заголовок, не требуется и не получится. Это разные способы обмена, их не нужно смешивать случайно.

В сопровождении важно видеть, где токен может попасть в логи, аналитику и отчёты об ошибках. Для диагностики обычно полезнее причина отказа, время события и идентификатор запроса, чем полная строка доступа. Рабочие ключи и токены не прикладывают к публичным задачам и скриншотам.

Как проверять ошибки и принимать реализацию

Отказные сценарии проверки JWT

Успешный вход показывает только один сценарий. Чтобы принять реализацию, нужно проверить и отказы, и жизненный цикл. Испытания проводят в согласованной тестовой среде, с подготовленными учётными записями и ожидаемым результатом. Приведённые ниже случаи являются планом проверки, а не результатами аудита нашего сайта.

Payload изменён, а подпись оставлена прежней. Запрос должен быть отклонён на этапе проверки защиты. Если приложение только декодирует строку и доверяет прочитанной роли, оно не выполнило необходимую проверку. В протоколе зафиксируйте фактический ответ, а не только сообщение интерфейса.

Токен подписан допустимым ключом, но предназначен другому API. Проверка подписи может пройти, однако назначение не соответствует ожидаемому. Это отдельный случай для проверки aud и профиля. Аналогично проверяют неподходящего издателя и токен другого вида.

Время действия не подходит. Подготовьте просроченный токен и токен, который ещё нельзя принимать. Если обязательное поле отсутствует, это тоже самостоятельный сценарий. Он помогает обнаружить разницу между «проверяем значение, когда оно есть» и «требуем поле в каждом принимаемом токене».

Токен действителен, но заказ принадлежит другому покупателю. Здесь проверяется уже доступ к объекту. Нужно проверить отказ в чтении и изменении, а также отсутствие данных чужого заказа в ответе. Скрытая кнопка в браузере не доказывает запрет серверной операции.

Пользователь вышел, права изменились или обновление перестало быть допустимым. Результат сравнивают с согласованными правилами отзыва. Проверьте ранее выданный access token, возможность получить новый и поведение интерфейса после отказа. Иначе можно принять красивый экран выхода при продолжающемся доступе к API.

Ответы на отсутствие токена, неверный токен и недостаточные права стоит согласовать в контракте REST API. Интерфейс должен отличать необходимость нового входа от запрета конкретного действия. Не стоит бесконечно повторять запросы обновления при любой ошибке сервера.

В протоколе приёмки сохраните версию приложения, условия, ожидаемый и фактический результат. После смены ключей, библиотек или интеграций существенные сценарии проверяют снова. Это можно включить в сопровождение сайта и его интеграций.

Для Гуси-Лебеди понятная реализация JWT начинается с описания поведения. Кто выпускает токен, кто проверяет, что именно разрешено и как прекращается доступ. Когда на эти вопросы есть ответы, длинная строка перестаёт выглядеть магией и становится частью проверяемой системы.

Начать проект

Обсудим вашу задачу

Ответим в течение рабочего дня. Можно без технического задания — достаточно цели и вводных.

Заполнить короткий бриф ↗
Навигация по экспертизе

Ещё направления для вашего проекта

Все 86 услуг →