В спецификации JSON Web Signature (JWS) и JSON Web Encryption (JWE), используемых в библиотеке jose, ключевая задача при верификации токена — корректно определить криптографический ключ, который был использован при подписании. Ошибка на этом этапе приводит либо к невозможности проверить подпись, либо к критическим уязвимостям, когда злоумышленник может подменить ключ или алгоритм.
Механизм выбора ключа в процессе проверки основывается на трёх полях
заголовка JWT: kid, alg, use. Эти
параметры формируют стратегию поиска ключа в хранилище и определяют,
какой именно ключ должен быть использован для проверки подписи.
JWT-заголовок содержит метаданные, влияющие на выбор криптографического материала:
alg — алгоритм подписи (например, HS256, RS256,
ES256)kid — идентификатор ключаuse — назначение ключа (подпись или шифрование)Каждое из этих полей играет отдельную роль, и их совместная обработка формирует безопасную стратегию верификации.
kid при выборе ключаkid (Key ID) — это основной механизм идентификации ключа
в наборах JSON Web Key Set (JWKS).
kidПри вызове проверки подписи библиотека выполняет следующую логику:
kid из заголовка JWT.kid совпадает.alg).const key = jwks.keys.find(k => k.kid === decoded.header.kid);
kid не гарантирует безопасность сам по себеalg возможны атаки подмены
алгоритмаkid должен приводить к ошибке
верификацииalg в
защите от атак на алгоритмПоле alg определяет алгоритм криптографической
операции:
При проверке библиотека обязана:
alg из JWT с допустимыми алгоритмами
ключаalgЕсли алгоритм не проверяется, возможны атаки типа:
use как ограничитель назначения ключаПоле use в JWK указывает назначение ключа:
sig — подписьenc — шифрованиеПри подборе ключа библиотека должна учитывать:
use: siguse: enc игнорируютсяИгнорирование use приводит к ситуации, когда:
kid, alg, useВ реальной реализации jose используется комбинированный подход:
kidJWKS → фильтрация по kid
use)оставить только ключи с use = sig
alg)сравнение JWT.alg с key.alg
ключ считается валидным только при совпадении всех условий
kidЕсли kid отсутствует:
kidkidПоведение:
kid найден, но alg не совпадаетРезультат:
use не
соответствуетРезультат:
Упрощённая модель внутри библиотеки:
function selectKey(jwks, header) {
return jwks.keys
.filter(k => k.use === 'sig')
.filter(k => k.kid === header.kid)
.filter(k => k.alg === header.alg)
.shift();
}
При работе с jose критично соблюдать следующие правила:
algАлгоритм нельзя доверять входящему JWT.
kid matchНе допускается частичное совпадение или fallback без контроля.
useКлючи должны быть строго разделены по назначению.
Перебор всех ключей без kid должен быть
контролируемым.
alg из токенаПриводит к атаке подмены алгоритма.
kidПриводит к выбору неправильного ключа при ротации.
useПриводит к смешению ключей подписи и шифрования.
Полный процесс проверки JWT в контексте ключевого выбора:
kid, algusekidalgПри смене ключей (key rotation):
kid до истечения
срокаkidКлюч считается валидным только при выполнении трёх условий одновременно:
kidalguseЛюбое нарушение этих условий приводит к отклонению токена без исключений.