Структура токена JWT: заголовок, полезная нагрузка и подпись
Структура токена JWT
Каждый JSON Web Token (JWT) — это компактная, безопасная для URL строка, которая передаёт claims между двумя сторонами. Понимание его внутренней структуры необходимо любому разработчику, работающему с безопасностью на основе токенов.
Три части JWT
JWT состоит из трёх частей, разделённых точками (.):
header.payload.signature
Пример:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Каждая часть — это JSON, закодированный в base64url. JWT подписан, а не зашифрован — любой может декодировать и прочитать его содержимое.
Часть 1: Заголовок (Header)
Заголовок содержит метаданные о токене — алгоритм подписи и тип токена.
| Поле | Описание | Примеры значений |
|-------|-------------|----------------|
| alg | Алгоритм подписи (обязательное) | HS256, RS256, ES256 |
| typ | Тип токена (необязательное) | JWT |
| kid | Идентификатор ключа (необязательное) | "key-1" |
{
"alg": "HS256",
"typ": "JWT"
}
Поле alg сообщает проверяющей стороне, какой алгоритм создал подпись. Несоответствие алгоритма — или принятие alg: "none" — является распространённой уязвимостью безопасности JWT.
Часть 2: Полезная нагрузка (Payload)
Полезная нагрузка содержит claims о пользователе и метаданные. Claims делятся на три категории:
Зарегистрированные claims
| Claim | Полное название | Назначение |
|-------|-----------|---------|
| iss | Issuer | Кто выдал токен |
| sub | Subject | Идентифицирует пользователя |
| aud | Audience | Предполагаемый получатель |
| exp | Expiration | Временная метка истечения токена |
| nbf | Not Before | Токен недействителен до этого времени |
| iat | Issued At | Временная метка создания токена |
| jti | JWT ID | Уникальный идентификатор токена |
Пример декодированной полезной нагрузки
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"exp": 1516242622,
"iss": "https://auth.example.com"
}
Публичные claims определены в реестре IANA или используют URI, устойчивые к коллизиям. Приватные claims — это пользовательские claims, согласованные между издателем и потребителем.
Часть 3: Подпись (Signature)
Подпись создаётся путём объединения закодированного заголовка, закодированной полезной нагрузки и секретного ключа с использованием алгоритма из заголовка. Она служит двум целям:
- Целостность — подтверждает, что токен не был изменён после подписания.
- Аутентификация — доказывает, что токен был подписан ожидаемой стороной.
Создание подписи HS256
HMACSHA256(
base64urlEncode(header) + "." + base64urlEncode(payload),
secret
)
Создание подписи RS256
RSASHA256(
base64urlEncode(header) + "." + base64urlEncode(payload),
privateKey
)
Base64url и стандартный Base64
| Аспект | Стандартный Base64 | Base64url |
|--------|-----------------|-----------|
| Символ 62 | + | - |
| Символ 63 | / | _ |
| Дополнение | = | Отсутствует |
Такое кодирование обеспечивает безопасность JWT для HTTP-заголовков, параметров запроса и URL-путей без процентного кодирования.
Полный процесс создания JWT
- Создайте JSON заголовка и закодируйте его в base64url.
- Создайте JSON полезной нагрузки и закодируйте его в base64url.
- Объедините с разделителем
.. - Подпишите выбранным алгоритмом и секретом.
- Закодируйте подпись в base64url.
- Объедините все три части с разделителями
..
Рекомендации по безопасности
- Никогда не храните секреты в полезной нагрузке. Полезная нагрузка закодирована, а не зашифрована.
- Всегда проверяйте подпись. Без проверки любой может подделать токены.
- Проверяйте заголовок
alg. Отклоняйтеalg: "none"и попытки понижения алгоритма. - Устанавливайте короткое время истечения. Используйте claims
expс интервалом в минутах или часах, а не месяцах.
Декодируйте токен JWT онлайн
Используйте инструмент JWT decoder для анализа любого токена. Вставьте его и мгновенно увидите декодированный заголовок, полезную нагрузку и алгоритм подписи. Вся обработка происходит в вашем браузере.