JWT (JSON Web Token) — это компактный формат передачи данных между сторонами в виде JSON-объекта, защищённого цифровой подписью. Токен может быть подписан с помощью HMAC (симметричный ключ) или RSA/ECDSA (асимметричная пара), что гарантирует целостность и подлинность данных. По данным IETF RFC 7519, 2015, JWT используется в миллионах приложений для аутентификации, безопасного обмена claims и как формат ID Token в OpenID Connect.
Главное
JSON Web Token (JWT) — это открытый стандарт (RFC 7519), определяющий компактный и самодостаточный способ передачи информации между сторонами в виде JSON-объекта. Информация в JWT называется claims — утверждения о субъекте (пользователе) и дополнительных атрибутах. Каждый claim — это пара ключ-значение: идентификатор пользователя, роль, срок действия, издатель.
JWT называется самодостаточным, потому что вся информация для проверки содержится внутри самого токена. Серверу не нужно обращаться к базе данных или внешнему хранилищу, чтобы убедиться в валидности токена — достаточно проверить подпись. Это свойство делает JWT идеальным для распределённых систем и микросервисной архитектуры, где несколько сервисов должны аутентифицировать запросы без общего хранилища сессий.
По данным Auth0, 2025, более 65% мобильных и веб-приложений используют JWT как основной формат токена для API-аутентификации, опережая случайные строки (opaque tokens) и сессионные идентификаторы.
JWT состоит из трёх частей, разделённых точками: header.payload.signature. Каждая часть представляет собой Base64url-закодированный JSON. Рассмотрим каждую часть подробно.
Header содержит два обязательных поля: alg (algorithm — алгоритм подписи) и typ (type — тип токена, всегда "JWT"). Алгоритм может быть симметричным (HS256 — HMAC с SHA-256) или асимметричным (RS256 — RSA с SHA-256, ES256 — ECDSA с P-256). Асимметричные алгоритмы предпочтительнее, поскольку позволяют клиенту проверять подпись без владения секретным ключом.
Пример декодированного header:
{
"alg": "RS256",
"typ": "JWT",
"kid": "key-id-1"
}
Payload содержит claims — утверждения о субъекте. Claims делятся на три типа: зарегистрированные (iss, sub, aud, exp, nbf, iat, jti), публичные (определяемые разработчиком в IANA Registry) и приватные (согласованные между сторонами). sub (subject) — уникальный идентификатор пользователя. exp (expiration) — timestamp истечения токена. iss (issuer) — издатель токена.
{
"sub": "user-abc-123",
"iss": "https://auth.example.com",
"aud": "my-mobile-app",
"exp": 1812345678,
"iat": 1812342078,
"role": "premium_user"
}
Signature создаётся путём применения алгоритма подписи к конкатенации header и payload с использованием секретного или приватного ключа. Формула: HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) для HMAC, или RSASHA256(...) для асимметричного алгоритма. Получатель вычисляет подпись тем же способом и сравнивает с полученной — если они совпадают, данные не были изменены.
Процесс работы с JWT состоит из двух фаз: создание (выпуск) токена сервером аутентификации и проверка токена клиентом или сервером ресурсов. Сервер аутентификации получает учётные данные пользователя, создаёт payload с claims и подписывает его. Полученный JWT отправляется клиенту в ответе на запрос логина или в теле ответа OAuth 2.0 / OpenID Connect.
В мобильных приложениях JWT используется следующим образом: после успешного входа пользователь получает access token в формате JWT. Приложение сохраняет его в защищённое хранилище (Keychain на iOS, EncryptedSharedPreferences на Android). При каждом запросе к API приложение добавляет заголовок Authorization: Bearer <token>. Сервер API проверяет подпись JWT, извлекает claims и на основе них принимает решение о доступе — без обращения к базе данных.
По данным Google Codelabs, 2025, использование JWT в Firebase Authentication сокращает количество запросов к серверу аутентификации на 40–60% по сравнению с сессионными токенами, так как данные проверяются локально на каждом микросервисе. Это особенно важно для архитектур с высокой нагрузкой, где каждая миллисекунда задержки влияет на пользовательский опыт. При 50 000 запросов в минуту переход на JWT может сэкономить до 10 серверных инстансов, обрабатывающих introspection-запросы.
JWT и Session Token решают одну задачу — аутентификацию запросов — но принципиально различаются по архитектуре. Session Token — это случайная строка-идентификатор, которая ссылается на данные сессии, хранящиеся на сервере (stateful). JWT — самодостаточный токен, содержащий все данные внутри себя (stateless).
| Параметр | JWT | Session Token |
|---|---|---|
| Хранение данных | Внутри токена (самодостаточный) | На сервере (сессионное хранилище) |
| Масштабирование | Не требует shared storage | Требует Redis/DB для multi-server |
| Отзыв токена | Сложный (нужен blacklist) | Простой (удалить сессию из БД) |
| Размер | Большой (500–2000 байт) | Маленький (16–64 байта) |
| Проверка подписи | Криптографическая | Нет (сравнение строк) |
JWT выигрывает в распределённых системах: микросервисы могут проверять токен локально без общего хранилища. Например, в архитектуре с пятью микросервисами каждый сервис проверяет JWT за 1–2 мс без сетевого вызова, в то время как session token требует обращения к централизованному Redis при каждом запросе, добавляя 10–30 мс задержки. Однако JWT сложно отозвать — если токен уже выпущен, он действителен до истечения срока. Session Token легко отозвать, удалив запись из БД или Redis.
Для мобильных приложений комбинированный подход — JWT с коротким сроком жизни (15–30 минут) и Refresh Token — даёт баланс между производительностью и безопасностью. JWT используется для доступа к API, а refresh token (обычно opaque) — для получения новых JWT. При компрометации JWT злоумышленник имеет доступ на 15–30 минут; при компрометации refresh token сессия блокируется через rotation и reuse detection.
Безопасность JWT зависит от правильной реализации. Самая распространённая уязвимость — атака "alg none": злоумышленник изменяет header токена на "alg": "none", и сервер, не проверив алгоритм, принимает поддельный токен. Защита: всегда проверять, что алгоритм в header соответствует ожидаемому (RS256, ES256), и отклонять токены с alg: none.
Уязвимости JWT включают также: слабый секретный ключ для HMAC (перебор за минуты), утечка приватного ключа (подпись любых данных от имени сервера), хранение чувствительных данных в payload (JWT не шифрует, только подписывает), атака через JWK header injection (внедрение собственного публичного ключа). Использование проверенных библиотек — Nimbus JOSE + JWT, jjwt (io.jsonwebtoken), PyJWT — снижает риск эксплуатации этих уязвимостей.
Дополнительная мера защиты — JWK Thumbprint (RFC 7638): привязка публичного ключа к токену через отпечаток (thumbprint) в header. Если сервер сохраняет ожидаемый thumbprint для каждого клиента, атака JWK header injection становится невозможной — сервер отклоняет любой ключ, не совпадающий с зарегистрированным. OAuth Security Workshop 2025 рекомендует JWK Thumbprint как обязательную защиту для всех JWT, используемых в финансовых и медицинских приложениях.
Библиотека jjwt (auth0/java-jwt) позволяет создавать и проверять JWT в Android-приложении за несколько строк. В примере ниже сервер генерирует токен с sub и role, а клиент проверяет подпись. Для безопасного хранения секретного ключа на сервере используйте переменные окружения или HSM (Hardware Security Module) — хранение ключа в коде или конфигурационном файле является грубой ошибкой безопасности.
val secret = "my-256-bit-secret-key-here"
val token = JWT.create()
.withSubject("user-abc-123")
.withIssuer("auth.example.com")
.withClaim("role", "premium_user")
.withExpiresAt(Date(System.currentTimeMillis() + 3600000))
.sign(Algorithm.HMAC256(secret))
// Отправка токена клиенту
println("JWT: $token")
fun verifyToken(token: String): Boolean {
return try {
val decoded = JWT.require(Algorithm.HMAC256(secret))
.withIssuer("auth.example.com")
.build()
.verify(token)
// Подпись верна, claims извлечены
println("Subject: ${decoded.subject}")
true
} catch (e: Exception) {
println("Token invalid: ${e.message}")
false
}
}
Часто задаваемые вопросы
Нет. JWT подписывается, а не шифруется — любой может декодировать Base64 payload и прочитать данные. Чувствительная информация (пароли, номера карт, персональные данные) должна передаваться только в зашифрованном виде через JWE (JSON Web Encryption).
Рекомендуется ES256 (ECDSA с P-256) — он обеспечивает эквивалентный RSA 2048-bit уровень безопасности при значительно меньшем размере подписи. Для совместимости с легаси-системами подходит RS256. HS256 (HMAC) требует безопасного обмена секретным ключом, что сложнее в распределённой архитектуре.
JWT нельзя отозвать напрямую — он действителен до exp. Решения: использовать короткий срок жизни (15–30 минут), вести blacklist отозванных jti (JWT ID) на сервере, или связывать токены с версией секретного ключа. Refresh token при этом отзывается стандартным способом — удалением из хранилища.
Bearer token — это концепция: любой токен, который предъявитель (bearer) может использовать для доступа. JWT — это конкретный формат токена. Bearer token может быть JWT, а может быть opaque string. JWT добавляет к концепции Bearer самодостаточность и криптографическую верификацию.
Типичный JWT с RS256 подписью занимает 500–2000 байт. Если payload содержит много кастомных claims или используется асимметричная подпись с большим ключом, размер может достигать 4–5 KB. Это значительно больше, чем session token (16–64 байта), что влияет на размер HTTP-заголовков.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также