JWT (JSON Web Token)

26 июля 2026 · ~12 мин чтения

протокол авторизация веб стандарт безопасность

JWT (JSON Web Token)

JWT — это компактный формат подписанной «бумажки», в которой сервер записывает
факты о клиенте (кто он, до какого времени валиден, что ему можно), отдаёт её
клиенту и потом верит ей без запроса в базу.

История

JWT родился как побочная ветка длинного разговора про идентичность в вебе.
В нулевых сессия обычно жила на сервере: пользователь логинился, сервер
записывал ему session_id в память или таблицу, клиент таскал этот id в
куке. Работает, пока сервер один. Как только серверов становится десять,
приходится либо клеить пользователя к одному узлу (sticky sessions), либо
класть сессии в общий Redis. И то, и другое — операционная боль.

Параллельно рос OAuth. Первая версия (2007) была про делегирование доступа,
но в ней токены были непрозрачными строками — сервер каждый раз ходил к
провайдеру и спрашивал «а этот токен ещё жив?». Медленно.

В 2010–2013 разные компании начали делать одно и то же: «а давайте
запишем данные о пользователе прямо в токен и подпишем его, чтобы
не бегать никуда». У Google было своё, у Microsoft своё. Чтобы это
работало между системами, нужен был общий формат.

Формат родил рабочая группа IETF JOSE (JSON Object Signing and
Encryption). Основные авторы — Mike Jones (Microsoft, автор
множества стандартов OpenID/OAuth) и John Bradley, при активном
участии Nat Sakimura (OpenID Foundation). Финальный документ —
RFC 7519, опубликован в мае 2015 года. Одновременно вышло
семейство: RFC 7515 (JWS — подпись), RFC 7516 (JWE — шифрование),
RFC 7517 (JWK — формат ключа), RFC 7518 (JWA — набор алгоритмов).
JWT — только «профиль» JWS для нужд аутентификации.

Практическое распространение обеспечил OpenID Connect (2014, до
финального RFC), который построен поверх OAuth 2.0 и использует JWT
для id_token. Именно поэтому «войти через Google» технически — это
почти всегда получить JWT.

Дальше JWT ушёл далеко за пределы веба: в мобильные приложения, в
межсервисную аутентификацию внутри микросервисов, в API-ключи облачных
провайдеров, в подписи webhook-ов. К 2020 году это стал одним из
самых узнаваемых форматов в индустрии — и одновременно самым
критикуемым: за десять лет накопилось много историй, как его
неправильно проверяли.

Что это такое

Технически JWT — это строка из трёх частей, разделённых точками:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IlBhc2hhIn0.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Первая часть — header (заголовок), обычно содержит тип (JWT) и
алгоритм подписи (HS256, RS256, ES256). Вторая — payload
(полезная нагрузка), обычный JSON-объект с полями (их называют
claims — «утверждения»). Третья — signature (подпись), MAC или
цифровая подпись первых двух частей.

Каждая часть кодируется в base64url — это обычный base64, но с
безопасными для URL символами (- и _ вместо + и /) и без
паддинга. Кодирование, важно, — не шифрование. Payload можно
раскодировать и прочитать любым: перейди на jwt.io,
вставь токен — увидишь всё, что там лежит.

Смысл трёх частей такой: header и payload — открытая информация,
подпись — доказательство, что их выдал именно тот, у кого есть
секретный ключ. Меняешь один байт в payload — подпись перестаёт
сходиться, токен ломается.

Ключевой момент, который отличает JWT от «сессии» в классическом
понимании: сервер не хранит его. Всё, что нужно для проверки —
секретный ключ (для HMAC) или публичный ключ (для RSA/ECDSA). Токен
приходит, сервер пересчитывает подпись, сравнивает — если сошлось и
exp не истёк, значит верим. Никаких походов в базу.

JWT отличается от непрозрачного токена (opaque token). Opaque —
это просто случайная строка, которая сама по себе ничего не
означает; смысл ей придаёт таблица на сервере. Пропало соединение с
базой — токены не работают. JWT самодостаточен. Плата за это —
токен нельзя «отозвать» так же легко: он валиден, пока не истёк.

JWT отличается от сессионной куки. Кука — это транспортный
механизм (браузер сам кладёт её в каждый запрос), а JWT — формат
данных. JWT можно возить в куке, в заголовке Authorization: Bearer,
в query-параметре, в теле POST. И наоборот, в куке может лежать что
угодно, не обязательно JWT.

Аналогии из жизни

Бумажный пропуск с печатью. Ты пришёл в бизнес-центр, тебе выдали
пропуск: «Иван Петров, до 18:00, этаж 5», внизу — печать охраны.
Охранник на 5-м этаже не звонит вниз проверять — он смотрит на печать
(её невозможно подделать) и на время (18:00 ещё не наступило). Пропуск
и есть JWT.

Где ломается: пропуск украли — и вор попадает на этаж 5, потому
что подпись всё ещё валидна. Именно поэтому в JWT ставят короткий
exp. И именно поэтому в важных системах пропуск проверяют не только
по печати, но и по фото — а в вебе к JWT прикручивают вторую проверку:
IP, отпечаток браузера, второй фактор.

Билет в кино. Ты купил онлайн, тебе прислали QR-код. Внутри QR —
все данные: фильм, зал, ряд, место, время сеанса, плюс подпись
кинотеатра. Билетёр сканирует, приложение говорит «валидный» — и
пропускает. Никаких звонков в кассу, вся правда внутри билета.

Где ломается: скриншот билета можно переслать другу. Он придёт
раньше тебя — сканер пикнет «валидный» и пустит. Кинотеатры решают это
через jti (уникальный идентификатор билета) плюс список «уже
использованных» — тот самый компромисс, из-за которого чистый JWT
не так уж и stateless. В веб-версии это блэклист отозванных токенов.

Заверенная нотариусом доверенность на автомобиль. Ты приехал на
чужой машине к гаишнику, показываешь бумагу: «Я, Владелец, разрешаю
Ивану ездить до 31 декабря». Гаишник не звонит владельцу — он проверяет
печать нотариуса (публичный ключ) и дату (exp). Если сходится — едешь
дальше. Именно так работают межсервисные JWT, подписанные ключом
identity-провайдера: сервис-получатель не знает лично identity-провайдера,
но знает его публичный ключ.

Где ломается: если владелец передумал — забрать доверенность у Ивана
уже невозможно. Она валидна до 31 декабря, и никакой отзыв через
интернет не поможет, если гаишник не подключён к базе отозванных
доверенностей. Отсюда — идея коротких JWT и refresh-токенов.

Как это работает

Шаг за шагом, на примере логина в веб-приложение.

Шаг 1. Клиент отправляет логин/пароль. POST на /auth/login с
{email, password}.

Шаг 2. Сервер проверяет пароль. Достаёт хеш из БД, сравнивает.
Если всё в порядке — переходит к выпуску токена.

Шаг 3. Сервер собирает payload. JSON-объект с claims:

{
  "sub": "user_42",
  "email": "pasha@example.com",
  "role": "admin",
  "iat": 1785840000,
  "exp": 1785843600
}

sub (subject) — кто, iat (issued at) — когда выдан, exp
(expiration) — до какого момента валиден (обычно 15 минут — 1 час).
Есть и другие стандартные claims: iss (issuer, кто выдал),
aud (audience, кому предназначен), nbf (not before, не валиден до),
jti (уникальный ID). Плюс любые свои поля.

Шаг 4. Сервер собирает header.

{"alg": "HS256", "typ": "JWT"}

alg — алгоритм подписи. typ — тип.

Шаг 5. Кодирование в base64url.

header_b64 = base64url(header_json)
payload_b64 = base64url(payload_json)

Шаг 6. Подпись. Считается MAC или цифровая подпись от
header_b64 + "." + payload_b64:

signature = HMAC_SHA256(secret_key, header_b64 + "." + payload_b64)
signature_b64 = base64url(signature)

Шаг 7. Склейка. Три части через точки:
header_b64.payload_b64.signature_b64 — это и есть JWT. Отдаём
клиенту в ответе (обычно в JSON: {"access_token": "..."}).

Шаг 8. Клиент хранит токен. Обычно в памяти (переменная в JS),
localStorage или в куке с флагами HttpOnly; Secure; SameSite.

Шаг 9. Клиент шлёт запрос. В заголовке:
Authorization: Bearer eyJhbGciOi...

Шаг 10. Сервер проверяет токен. Разбирает три части, пересчитывает
подпись с тем же секретом — сравнивает. Если сошлось, читает payload и
проверяет exp (текущее время меньше exp?). Если оба ответа «да» —
запрос принят, sub — это пользователь, role: admin — можно делать
админские вещи.

Никаких обращений к базе, кроме собственно бизнес-логики. В этом и
сила: сервис аутентификации может стоять отдельно, выпустить токен и
уйти в отпуск — а сервисы, принимающие токен, продолжают работать сами.

Для асимметричных алгоритмов (RS256, ES256) картинка немного
другая: сервер, выпускающий токен, держит приватный ключ, а все
сервисы-приёмники знают только публичный. Публичный обычно
раздаётся через JWKS-эндпоинт (.well-known/jwks.json) — JSON со
списком ключей, которые провайдер сейчас использует. Приёмник забирает
эту раздачу, кэширует, проверяет подписи. Это позволяет провайдеру
менять ключи (rotate) без необходимости всем всё пересылать.

Где встречается в обычной жизни

Где встречается в IT и бизнесе

Кто пользуется

Практически все крупные интернет-компании. Auth0 (сейчас часть
Okta) — исторически один из главных драйверов популяризации JWT,
специализированный сервис на этом; используют десятки тысяч клиентов.
Firebase Authentication от Google — миллионы приложений, каждый
логин отдаёт JWT. AWS Cognito — стандартный auth-сервис Amazon,
внутри JWT. Keycloak — популярный open-source identity-сервер
(изначально Red Hat), выдаёт JWT. Okta, Azure AD (сейчас
Microsoft Entra ID) — корпоративные SSO с JWT под капотом.

Из «продуктовых» примеров: GitHub Apps авторизуются в GitHub API
через подписанный своим приватным ключом JWT. Stripe использует
JWT для сессий в дашборде. Slack, Notion, Figma — везде
внутри веб-приложения токены доступа в формате JWT или его
диалекта.

По масштабам: типичный крупный сервис проверяет десятки тысяч JWT в
секунду
— и это дешёвая операция (миллисекунды), потому что не
требует I/O.

Альтернативы и конкуренты

Когда НЕ стоит использовать

Связанные понятия

Литература и источники

Где встретилось у меня

Разбирался с API одного крупного объявлений-сайта: чтобы получить
подменный номер телефона, нужно было сначала дёрнуть один эндпоинт
(он возвращал JWT со сроком жизни в несколько секунд и «правом»
запросить конкретный лот), а потом передать этот JWT в query-параметр
второго эндпоинта. Внутри токена были стандартные claims — идентификатор
объекта, идентификатор сессии, exp. Именно эта пара «получи короткий
токен → используй его на другом эндпоинте» подсветила, зачем JWT нужен:
никакого стейта между двумя разными хостами, просто подписанная
короткоживущая бумажка.

Краткое резюме