Что внутри JWT и почему декодировать — не значит проверить
JSON Web Token (JWT) — это три фрагмента текста, соединённые точками: заголовок, полезная нагрузка и подпись. Первые два лишь закодированы, а не зашифрованы, поэтому любой, у кого есть токен, может их прочитать. Подпись позволяет серверу проверить, что токен выдала доверенная сторона и что его никто не менял. Декодирование показывает, что написано в токене. Только проверка подсказывает, можно ли ему верить.
Декодер JWTТокен истёк или ещё действителен?Сделать бесплатноТри части
Токен выглядит так: header.payload.signature. Первая часть известного примера токена с jwt.io:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Каждая из первых двух частей — это JSON, закодированный в base64url, формат текста, безопасный для URL. Первая часть декодируется в:
{"alg":"HS256","typ":"JWT"}
Полезная нагрузка того же примера декодируется в:
{"sub":"1234567890","name":"John Doe","iat":1516239022}
Полезная нагрузка содержит утверждения (claims) о пользователе или сессии. Подпись вычисляется из первых двух частей и ключа, поэтому изменение одного символа делает её неверной. Base64url обратить может любой без ключа, поэтому никогда не кладите в полезную нагрузку пароль, закрытый ключ или персональные данные.
Стандартные утверждения
RFC 7519, стандарт для JWT, резервирует несколько коротких имён утверждений:
iss(издатель): кто создал токен.sub(субъект): к кому относится токен, обычно идентификатор пользователя.aud(аудитория): сервис, для которого предназначен токен.exp(время истечения): момент, после которого токен недействителен.nbf(не раньше): самый ранний момент, когда токен действителен.iat(время выдачи): когда токен был создан.
Время задаётся в секундах с 1 января 1970 года UTC, так называемое время Unix. В примере нет exp, поэтому по стандарту он никогда не истекает. У настоящего токена он должен быть. Другие имена, например role или email, определяет приложение.
Декодирование — не проверка
Декодирование разбивает токен по точкам, обращает base64url и читает JSON. Так видно, что токен утверждает, но так же видно и у поддельного токена, ведь любой может написать любые утверждения с любой подписью.
Проверка пересчитывает подпись правильным ключом и сверяет её с полученной. Также она проверяет, что текущее время находится между nbf и exp, а iss и aud совпадают со значениями, которые ожидает сервис. Приложение, которое читает утверждение role без проверки, доверяет тому, кто написал токен. Это распространённая ошибка.
HS256, RS256 и ES256
Значение alg называет метод. Распространены три:
- HS256 — HMAC с SHA-256. Один общий секрет и подписывает, и проверяет, поэтому секрет должен оставаться на сервере, который выдаёт токены.
- RS256 — подпись RSA с SHA-256. Издатель подписывает закрытым ключом, а проверить может любой, у кого есть открытый ключ. Другие сервисы могут проверять токены, но не могут их создавать.
- ES256 — подпись ECDSA на кривой P-256 с SHA-256. Работает как RS256, но с более короткими ключами и подписями.
Используйте HS256, если один сервис и выдаёт, и проверяет свои токены. Используйте RS256 или ES256, если другим сервисам нужно проверять токены, которые они не выдавали.
Проблемы, возникающие из доверия заголовку
Заголовок пишет тот, кто отправляет токен, поэтому проверяющая сторона не должна позволять ему выбирать правила.
alg: none. Токен без подписи может объявить"alg":"none". Некоторые библиотеки когда-то принимали такие токены и пропускали проверку. Задавайте разрешённые алгоритмы в своём коде, никогда не берите их из токена.- Подмена алгоритма. Сервер ожидает RS256 и хранит открытый ключ. Злоумышленник отправляет токен HS256, подписанный этим открытым ключом как HMAC-секретом. Проверяющая сторона, которая принимает любой алгоритм из заголовка, его примет. Привязывайте каждый ключ ровно к одному алгоритму.
- Слабые секреты. У злоумышленника есть заголовок, полезная нагрузка и подпись, поэтому он может перебирать кандидатов в секреты офлайн. Короткий секрет или распространённое слово быстро падают под такой список. RFC 7518 требует, чтобы ключ HS256 был не короче 256 бит и случайным.
Вставка рабочего токена на сайт
Действительный, не истёкший токен работает для того, кто его держит, пока не истечёт срок. Относитесь к нему как к паролю. Прежде чем вставить токен на сайт, выясните, отправляет ли сайт его на сервер. Инструменту, который работает целиком во вкладке браузера, это не нужно. Любую страницу можно проверить, наблюдая сетевые запросы в инструментах разработчика браузера.
Токены обычно нельзя отозвать до истечения срока, если сервер не ведёт список отозванных. Если действующий токен утёк, попросите сервис отозвать сессию или заменить ключи. Где возможно, для отладки используйте токен из тестовой среды. Декодер JWT читает три части во вкладке вашего браузера, и ничего из вставленного никуда не отправляется.
Вебхуки: та же идея с общим секретом
Большинство провайдеров вебхуков отправляют в заголовке HMAC тела запроса, вычисленный с секретом, которым вы делитесь с ними. Ваш получатель вычисляет тот же HMAC и сравнивает два значения. Типичные ошибки мелкие. JSON перед хешированием переформатируют, и байты меняются. Тестовый секрет применяют к боевому трафику. Или подписи сравнивают обычным строковым равенством, что может утечь информацией о времени. Используйте сравнение за постоянное время.
Проверка подписи вебхука вычисляет подпись по вставленным секрету и телу для GitHub, Stripe, Slack, Shopify или любой схемы HMAC, так что вы видите, совпадают ли два значения.
Быстрые ответы
JWT зашифрован?
Нет. Стандартный JWT подписан, и любой может прочитать его заголовок и полезную нагрузку. Защищает его от изменений только подпись.
Что будет, если я изменю полезную нагрузку?
Подпись перестанет совпадать, поэтому проверяющий сервер отвергнет токен. Чтение утверждений не проверяет подпись, так что декодер всё равно покажет ваш изменённый вариант.
Переведено с помощью ИИ с Английский оригинал.
Комментарии
Комментариев пока нет. Будьте первым.


