Что такое JWT? Понятное руководство по JSON Web Tokens
Узнайте, что такое JWT (JSON Web Token), как работают его три части, где он используется и ключевые советы по безопасности, чтобы избежать распространённых ошибок с токенами.

JWT простыми словами
JWT (JSON Web Token) — это компактная, URL-безопасная строка, которая представляет набор информации (обычно о пользователе или сессии) и может передаваться между системами. Вы часто увидите её как длинное значение, начинающееся с eyJ..., отправляемое в HTTP-заголовке, например Authorization: Bearer \u003ctoken\u003e.
Зачем вообще нужен токен?
Традиционные логины часто опираются на серверные сессии: вы входите, сервер сохраняет данные сессии и отдаёт браузеру cookie с ID сессии. Каждый запрос включает эту cookie, и сервер смотрит запись сессии.
С аутентификацией на основе токенов сервер может избежать хранения состояния для каждого запроса пользователя. Клиент держит токен (например, JWT) и прикладывает его к вызовам API. Это популярно для API, потому что:
- хорошо работает между множеством сервисов (API gateway, микросервисы)
- подходит для мобильных приложений и одностраничных приложений (SPA), которые напрямую вызывают API
- снижает необходимость в общем хранилище сессий между серверами
Важное уточнение: «stateless» не означает «никаких серверных проверок». Многие реальные системы всё ещё дополнительно проверяют токены на предмет статуса пользователя, ротации ключей или механизмов отзыва.
Аутентификация vs авторизация (простыми словами)
- Аутентификация отвечает: Кто вы? (Вы входите и доказываете свою личность.)
- Авторизация отвечает: Что вам разрешено делать? (Чтение счетов, редактирование проектов, доступ к админке и т.д.)
JWT часто несут в себе доказательство аутентификации (вы вошли) и подсказки для авторизации (роли, права, scope), — но сервер всё равно должен применять правила авторизации.
Где встречаются JWT
JWT обычно используются как access tokens в:
- веб-API
- SPA
- мобильных приложениях
- системах с OAuth 2.0 или OpenID Connect (OIDC)
Структура JWT: header, payload и signature
JWT — компактная строка из трёх частей, каждая base64url-encoded и разделённая точками:
header.payload.signature
Пример (редактировано):
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiaWF0IjoxNzAwMDAwMDAwfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c…
1) Header
Header описывает, как был создан токен — главным образом алгоритм подписи (например, HS256, RS256/ES256) и тип токена.
Обычные поля:
typ: часто"JWT"(часто игнорируется на практике)alg: алгоритм подписиkid: идентификатор ключа, помогающий выбрать нужный ключ при ротации
Заметка по безопасности: не доверяйте header слепо. Применяйте allowlist алгоритмов, которые вы действительно используете, и не принимайте alg: "none".
2) Payload
Payload содержит "claims" — поля о пользователе и контексте токена: для кого он, кто выпустил и когда истекает.
Важно: JWT по умолчанию не шифруются. Base64url делает токен безопасным для URL; он не скрывает данные. Любой, у кого есть токен, может декодировать header и payload.
Поэтому не храните в JWT секреты (пароли, API-ключи) или чувствительные персональные данные.
3) Signature
Подпись создаётся путём подписания header + payload с использованием ключа:
- HS256: общий секрет для подписи и проверки
- RS256/ES256: приватный ключ подписывает; публичный ключ проверяет
Подпись обеспечивает целостность: сервер убеждается, что токен не был изменён и выпущен доверенным подписантом. Подпись не обеспечивает конфиденциальность.
Размер токена
Поскольку JWT включает header и payload в каждом запросе, где он отправляется, большие токены увеличивают трафик и нагрузку. Держите claims минимальными и предпочитайте идентификаторы вместо громоздких данных.
Payload и claims: что можно и чего нельзя хранить
Claims делятся на две категории: зарегистрированные (стандартизированные имена) и пользовательские (поля вашего приложения).
Частые зарегистрированные claims
iss(issuer): кто создал токенsub(subject): о ком токен (часто ID пользователя)aud(audience): для кого предназначен токен (например, конкретное API)exp(expiration time): когда токен должен перестать приниматьсяiat(issued at): когда токен созданnbf(not before): токен не должен приниматься до этого времени
Пользовательские claims: держите их минимальными
Включайте только то, что реально нужно принимающему сервису для принятия решения об авторизации.
Хорошие примеры:
- стабильный внутренний идентификатор пользователя (
user_id) - небольшой набор ролей/прав (только если вы можете их поддерживать в актуальном состоянии)
- tenant/organization ID для мультиарендных приложений
Избегайте "удобных" claims с копированием профиля: они раздувают токен, быстро устаревают и увеличивают последствия утечки.
Что никогда не стоит помещать в JWT payload
Поскольку payload читаем, не храните:
- пароли, API-ключи, refresh-токены или любые секреты
- платежные данные, номера документов или чувствительные персональные данные
- всё, что вы не хотели бы увидеть в браузере, прокси или логах
Если нужна чувствительная информация, храните её на сервере и в токен кладите только ссылку (ID), либо используйте зашифрованный формат токена (JWE) при необходимости.
Как работает подпись (и что она гарантирует)
Подпись — это не шифрование.
- Подпись похожа на опечатанный конверт: можно прочитать письмо, но можно проверить, не меняли ли его содержимое.
- Шифрование похоже на запирание письма: читать может только тот, у кого есть ключ.
При выпуске JWT сервер подписывает закодированные header + payload. При предъявлении токена позже сервер пересчитывает подпись и сравнивает. Если кто-то изменит хоть один символ (например, "role":"user" на "role":"admin"), верификация упадёт и токен отвергнут.
JWT vs OAuth, OpenID Connect и типы токенов
JWT — это формат токена. OAuth 2.0 и OpenID Connect (OIDC) — протоколы, описывающие, как приложения запрашивают, выдают и используют токены.
OAuth 2.0 — access/refresh токены
OAuth 2.0 в основном про авторизацию: дать приложению доступ к API от имени пользователя без передачи пароля.
- Access token: предъявляется API, чтобы доказать разрешение; может быть JWT или opaque
- Refresh token: долгоживущий токен для получения новых access-токенов
Access-токены обычно короткоживущие (минуты). Короткий срок ограничивает ущерб при утечке.
OpenID Connect (OIDC) и ID-токены
OIDC добавляет аутентификацию (подтверждение личности) поверх OAuth 2.0 и вводит ID token, который обычно является JWT.
- ID token: для клиентского приложения — чтобы подтвердить личность пользователя
- Access token: для API — чтобы авторизовать запросы
Правило: не используйте ID token для вызова API.
Если нужно больше примеров потоков, см. /blog/jwt-authentication-flow.
Обычный поток аутентификации с JWT
Типичный поток выглядит так:
1) Вход
Пользователь входит (email/password, SSO и т.д.). При успехе сервер создаёт JWT (часто это access token) с базовыми claims, такими как subject и expiration.
2) Выдача токена
Сервер подписывает токен и возвращает его клиенту (веб-приложение, мобильное приложение или другой сервис).
3) Вызовы API
Для защищённых эндпоинтов клиент включает JWT в заголовок Authorization:
Authorization: Bearer \u003cJWT\u003e
4) Верификация
Перед обслуживанием запроса API обычно проверяет:
- подпись (целостность + надёжный издатель)
exp(не просрочен)iss(ожидаемый issuer)aud(предназначен для этого API)
Если все проверки проходят, API считает пользователя аутентифицированным и применяет правила авторизации (например, уровень доступа к записям).
5) Небольшая заметка про дрейф часов
Поскольку часы на системах расходятся, многие системы допускают небольшой clock skew при проверке временных claims, таких как exp и nbf. Держите допуск небольшим, чтобы не увеличивать фактическое время жизни токена больше задуманного.
Где безопасно хранить JWT
Выбор места хранения меняет, какие атаки возможны и насколько легко можно повторно использовать токен.
Браузер: память vs localStorage vs cookies
Хранение в памяти (рекомендуется для SPA) — держите access-токен в JS-памяти. Он очищается при перезагрузке и снижает риск «взять позже», но XSS во время выполнения страницы всё ещё может его прочитать. Используйте короткоживущие access-токены и поток обновления.
localStorage/sessionStorage просты, но рискованы: любой XSS может вытянуть токены. Если используете, профилактика XSS обязателна (CSP, экранирование, проверка зависимостей), и держите токены короткоживущими.
Secure cookies (часто самый безопасный выбор для веба) — храните токены в HttpOnly cookie, чтобы JavaScript не мог их прочитать. Минус — риск CSRF, так как браузер автоматически прикладывает cookie.
Если используете cookies, установите:
HttpOnlySecure(только по HTTPS)SameSite=LaxилиSameSite=Strict(иногда для кросс-сайтовых потоков нуженSameSite=None; Secure)
Также рассмотрите CSRF-токены для запросов, изменяющих состояние.
Мобильные приложения: используйте защищённое хранилище ОС
На iOS/Android храните токены в безопасном хранилище платформы (Keychain / Keystore). Избегайте простых файлов или настроек. Если угроза включает рутованные/джейлбрейкнутые устройства, предполагайте, что извлечение возможно — полагайтесь на короткоживущие токены и серверные механизмы контроля.
Принцип наименьших привилегий
Ограничивайте возможности токена: минимальные scope/claims, короткое время жизни access-токенов, и избегайте вложения чувствительных данных.
Типичные ошибки безопасности JWT, которых стоит избегать
JWT удобны, но многие инциденты происходят из-за предсказуемых ошибок. Относитесь к JWT как к наличным деньгам: кто получил — тот может потратить.
1) Слишком длительный срок жизни
Если токен живёт дни или недели, утечка даёт атакующему всё это окно. Предпочитайте короткоживущие access-токены (минуты) и обновляйте их через безопасный механизм. Для «запомнить меня» используйте refresh-токены и серверные проверки.
2) Отсутствие проверок issuer и audience
Подпись недостаточна. Проверяйте iss и aud, валидируйте временные claims, такие как exp и nbf.
3) Доверие декодированному payload
Декодирование — это не верификация. Всегда проверяйте подпись на сервере и применяйте авторизационные политики серверной стороны.
4) Путаница с алгоритмами и ключами
- Не принимайте любой алгоритм из header. Делайте allowlist.
- Не смешивайте симметричные ключи (HS256) и асимметричные (RS256/ES256).
- Снижайте blast radius, разделяя ключи между окружениями и проводя ротацию.
5) Утечка токенов через URL, логи и referrer
Избегайте размещения JWT в query-параметрах. Они могут попасть в историю браузера, логи, аналитические сервисы и referrer-заголовки.
Используйте Authorization: Bearer ... вместо этого.
6) Отсутствие плана ротации и отзыва ключей
Предполагайте, что ключи и токены могут утечь. Проводите ротацию signing-ключей, используйте kid для плавной ротации и имейте стратегию отзыва (короткие сроки жизни + возможность отключать аккаунты/сессии). Для советов по хранению смотрите /blog/where-to-store-jwts-safely.
Когда стоит использовать JWT (и когда нет)
JWT полезны, но не всегда лучший выбор. Вопрос: приносит ли вам пользу самодостаточный токен, который можно проверить без обращения к базе при каждом запросе?
Подходящие случаи для JWT
- Статeless API в масштабе: локальная проверка (подпись + expiry) без запроса сессии на каждый запрос
- Множество сервисов / микросервисы: общие правила валидации и публичные ключи
- SPA и мобильные приложения: клиенты вызывают API напрямую
- Короткоживущие access-токены: уменьшают ущерб при утечке
Когда JWT — плохой выбор
- Требуется мгновенный отзыв: сессии проще, если нужно «выйти везде сейчас» без дополнительной инфраструктуры
- Нужно хранить чувствительные данные: обычные JWT подписываются, но не шифруются
- Долгоживущие токены: они дорогие для безопасности и стоят того, чтобы их не использовать
Когда лучше простые cookie-сессии
Для традиционных серверно-рендеренных приложений, где важно простое инвалидирование, серверные сессии с HttpOnly cookies чаще проще и безопаснее.
Короткий чек-лист для решения
Выбирайте JWT, если вам нужна статeless-проверка между сервисами и вы можете держать токены короткоживущими.
Избегайте JWT, если нужна мгновенная отзывность, вы собираетесь помещать чувствительные данные в токен или можете использовать cookie-сессии без неудобств.
Практический чек-лист и FAQ
Чек-лист валидации (что проверять каждый раз)
- Подпись валидна
Проверяйте правильный ключ и ожидаемый алгоритм. Отклоняйте неверные подписи — никаких исключений.
exp(срок действия)
Убедитесь, что токен не просрочен.
nbf(not before)
Если присутствует, убедитесь, что токен не используется раньше времени.
aud(audience)
Подтвердите, что токен предназначен для вашего API/сервиса.
iss(issuer)
Подтвердите, что токен выпущен ожидаемым издателем.
- Sanity checks (рекомендуется)
Проверяйте формат токена, максимальный размер и отвергайте неожиданные типы claims, чтобы снизить риск пограничных ошибок.
Выбор HS256 vs RS256/ES256
-
HS256 (симметричный ключ): один общий секрет для подписи и проверки.
- Подходит для: единого приложения/API, управляемого одной командой.
- Минус: любой, у кого есть секрет, может и подписывать токены.
-
RS256 / ES256 (асимметричные ключи): приватный ключ подписывает; публичный ключ проверяет.
- Подходит для: множества сервисов, которые проверяют токены; распространение публичного ключа не даёт возможности подписывать.
- Операционно: ротация часто безопаснее, потому что подписывает только приватный ключ.
Правило: если более одной независимой системы должна проверять токены (или вы не полностью доверяете каждому верификатору), предпочитайте RS256/ES256.
Мониторинг и логирование (без утечек токенов)
- Не логируйте сырые токены (в заголовках, cookie, query-строках).
- Если нужен корелляционный идентификатор, логируйте фингерпринт токена (например, хеш) или безопасную метаинформацию (
iss,aud, user ID только если политика позволяет). - Следите за аномалиями: неудачные подписи, всплески просроченных токенов, необычные audience/issuer и подозрительные паттерны обновления.
FAQ
JWT зашифрован?
Не по умолчанию. Большинство JWT подписаны, но не зашифрованы — содержимое читаемо при наличии токена. Используйте JWE или не храните чувствительные данные в JWT.
Можно ли отозвать JWT?
Сложно, если вы полагаетесь только на самодостаточные access-токены. Распространённые подходы: короткоживущие access-токены, deny-list в критичных случаях, или refresh-токены с ротацией.
Какой должен быть срок exp?
Как можно короче, при условии приемлемого UX и архитектуры. Многие API используют минуты для access-токенов и refresh-токены для продолжительных сессий.
Быстрее создавать приложения с защитой JWT с помощью Koder.ai
Если вы внедряете JWT-аутентификацию в новый API или SPA, много работы повторяется: подключение middleware, проверка iss/aud/exp, установка флагов cookie и исключение обработки токенов в логах.
С Koder.ai вы можете быстро прототипировать веб-приложение (React), бэкенд (Go + PostgreSQL) или Flutter-приложение через чат-ориентированный рабочий процесс — затем итеративно планировать, использовать снимки и откаты при отладке безопасности и экспортировать исходный код, когда будете готовы. Это практичный способ ускорить реализацию JWT-аутентификации, сохраняя контроль над логикой верификации, стратегией ротации ключей и настройками деплоя (включая кастомные домены).
FAQ
Что такое JWT и куда я обычно его отправляю?
JWT (JSON Web Token) — это компактная, URL-безопасная строка, которая несёт «claims» (поля данных) и может быть проверена сервером. Обычно её отправляют в API-запросах через:
Authorization: Bearer <token>
Главная идея: сервер может проверить целостность токена (по подписи) без необходимости хранить сессию для каждого запроса.
Чем JWT-аутентификация отличается от серверных сессий?
Аутентификация через сессии обычно хранит состояние на сервере (запись сессии, идентифицируемая cookie/ID сессии). При JWT-аутентификации клиент прикладывает подписанный токен к каждому запросу, а API его валидирует.
JWT популярны для API и распределённых приложений, потому что проверку можно выполнить локально, уменьшая потребность в общем хранилище сессий.
«Бессерверность» не означает отсутствие серверных проверок: часто добавляют deny-list, проверки статуса пользователя или обработку ротации ключей.
Какие три части у JWT (header, payload, signature)?
JWT состоит из трёх частей Base64URL-кодированных и разделённых точками:
header.payload.signature
Header описывает алгоритм подписи, payload содержит claims (например, sub, exp, aud), а подпись позволяет серверу обнаружить подделку.
JWT зашифрован и можно ли читать его содержимое?
Нет. Стандартные JWT обычно подписаны, но не зашифрованы.
- Подпись подтверждает целостность и происхождение (не была ли изменена).
- Любой, у кого есть токен, может Base64URL-декодировать header и payload и прочитать содержимое.
Если нужна конфиденциальность, используйте JWE (шифрованные токены) или храните чувствительные данные на сервере, оставляя в токене только ссылку/ID.
Что гарантирует подпись JWT — и чего она не гарантирует?
Подпись позволяет серверу убедиться, что токен не был изменён и его выпустил доверенный подписант.
Она не даёт:
- конфиденциальности содержимого;
- гарантии, что пользователь всё ещё активен (если вы это не проверяете);
- немедленной отмены токена до
exp.
Относитесь к токену как к учетным данным: если он скомрометирован, его можно воспроизвести до истечения срока действия.
Что такое `alg` и `kid` в заголовке JWT и почему это важно?
alg указывает алгоритм, использованный для подписи (например, HS256 или RS256). kid — идентификатор ключа, помогающий подобрать правильный ключ при ротации.
Рекомендации по безопасности:
- Делайте allowlist ожидаемых алгоритмов; не принимайте произвольные
alg. - Никогда не принимайте
alg: "none". - Не позволяйте недоверенному
kidприводить к небезопасному поиску ключа.
Какие claims стоит включать в payload JWT?
Начните со стандартных зарегистрированных claims и держите собственные поля минимальными.
Частые зарегистрированные claims:
iss(issuer) — кто выпустил токенsub(subject) — субъект / идентификатор пользователяaud(audience) — для кого предназначен токен (API)exp(expiration) — время окончания действияiat(issued at) — время выпускаnbf(not before) — токен не пригоден до этого времени
Избегайте хранения секретов и чувствительных персональных данных в payload: всё, что в токене — читаемо при утечке.
Как соотносятся JWT, OAuth 2.0 и OpenID Connect (access token vs ID token)?
JWT — это формат токена; OAuth 2.0 и OpenID Connect — протоколы.
Типичное соответствие:
- Access token: для вызова API (может быть JWT или opaque).
- ID token (OIDC): для клиента, чтобы подтвердить личность (обычно JWT).
- Refresh token: для получения новых access-токенов (часто opaque и очень чувствителен).
Важно: не используйте ID token для вызова API просто потому, что он «выглядит как JWT».
Где безопасно хранить JWT в браузерном приложении?
Для браузерных приложений распространённые варианты хранения:
- В памяти: рекомендуется для SPA — токен хранится в JS-памяти. Очищается при перезагрузке, но XSS во время работы страницы всё ещё может украсть его. Сочетайте с коротким временем жизни access-токена и безопасным механизмом обновления.
- localStorage / sessionStorage: удобны, но уязвимы для XSS. Если используете их, предотвращение XSS — критично (CSP, экранирование вывода, контроль зависимостей), и держите токены короткоживущими.
- HttpOnly Secure cookies: часто самый безопасный вариант для веба — JS не может их прочитать. Минус — риск CSRF, поскольку браузер автоматически прилагает cookie. Используйте
HttpOnly,SecureиSameSiteи при необходимости CSRF-токены для изменяющих состояние запросов.
В любом случае держите access-токены короткоживущими и минимизируйте права.
Какие проверки должен выполнять API при валидации JWT?
Минимум проверок при валидации JWT:
- подпись (правильный ключ и allowlisted алгоритм)
exp(не истёк)iss(ожидаемый издатель)aud(предназначен для вашего API)nbf(если есть)
Практические дополнения:
- ограничьте максимальный размер токена
- отвергайте неожиданные типы claims
- допуск небольшого дрейфа времени (clock skew) чтобы компенсировать расхождения часов