JWT (JSON Web Token) широко используется для аутентификации и
передачи утверждений между сторонами. Его популярность связана с
простотой, компактностью и возможностью криптографической подписи.
Однако сама спецификация допускает режимы, которые при неправильной
реализации превращаются в критическую уязвимость. Один из наиболее
опасных примеров — использование алгоритма none, при
котором подпись фактически отсутствует.
JWT состоит из трёх частей:
JWT = HMAC;or;RSA;Signature;over(Header + “.” + Payload)
В заголовке указывается алгоритм подписи:
{
"alg": "HS256",
"typ": "JWT"
}
Именно поле alg определяет, как должна проверяться
подпись. В корректной реализации сервер обязан строго следовать
заявленному алгоритму и не доверять входным данным.
none и его смысл в спецификацииВ ранних версиях стандарта JSON Web Token предусматривалась возможность отсутствия подписи. Для этого использовался алгоритм:
{
"alg": "none"
}
В этом случае третья часть JWT либо пустая, либо отсутствует. Формально это выглядело так:
header.payload.
Идея заключалась в возможности использовать JWT как просто контейнер данных без криптографической защиты. Однако в реальных системах это почти всегда недопустимо, особенно в контексте авторизации.
alg: noneПроблема возникает, когда сервер:
alg из токенаnoneВ этом случае атакующий может:
alg: noneПример поддельного токена:
Header:
{
"alg": "none",
"typ": "JWT"
}
Payload:
{
"user": "admin",
"role": "administrator"
}
И итоговая строка:
base64(header).base64(payload).
Если сервер принимает такой токен, он фактически полностью отключает механизм аутентификации.
Основная причина — ошибки реализации:
decode вместо
verifyalg из токенаОсобенно опасно, когда разработчики считают JWT «самодостаточной защитой», не учитывая необходимость строгой криптографической проверки.
Библиотека Jsrsasign предоставляет инструменты для работы с JWT через
KJUR.jws.JWS.
Базовая проверка выглядит так:
const isValid = KJUR.jws.JWS.verifyJWT(
token,
publicKey,
{
alg: ['HS256']
}
);
Ключевой момент — явное указание допустимых алгоритмов.
Ошибочная реализация:
KJUR.jws.JWS.verifyJWT(token, key, {});
или даже:
KJUR.jws.JWS.verifyJWT(token, key);
В таком случае библиотека может:
noneЭто открывает возможность подделки токена.
alg: none
в JsrsasignВсегда необходимо явно задавать список разрешённых алгоритмов:
const result = KJUR.jws.JWS.verifyJWT(token, publicKey, {
alg: ['HS256', 'RS256']
});
Алгоритм none при этом автоматически исключается.
Дополнительный уровень защиты — ручная проверка header:
const headerObj = KJUR.jws.JWS.parse(token).headerObj;
if (headerObj.alg === 'none') {
throw new Error('Недопустимый алгоритм');
}
Это предотвращает даже попытку обхода логики проверки.
algГлавное правило безопасности JWT:
алгоритм никогда не должен определяться на основе входного токена
Сервер должен:
alg из токенаПри работе с RSA:
const isValid = KJUR.jws.JWS.verifyJWT(token, publicKey, {
alg: ['RS256']
});
Если используется HMAC:
alg: ['HS256']
Любые другие алгоритмы должны быть явно запрещены.
none на уровне логикиДаже если библиотека технически поддерживает none, его
нужно исключить архитектурно:
const allowedAlgs = new Set(['HS256', 'RS256']);
if (!allowedAlgs.has(headerObj.alg)) {
throw new Error('Алгоритм не разрешён');
}
Если сервер доверяет JWT без проверки подписи:
{
"user": "attacker",
"role": "admin"
}
Атака позволяет получить административный доступ.
В REST API часто используется проверка:
if (decoded.role === 'user') allow();
При alg: none атакующий просто подменяет payload.
Некоторые системы сначала читают alg, а затем:
Это критическая ошибка архитектуры.
В Jsrsasign разбор JWT включает:
Если алгоритм не ограничен, библиотека может:
Это делает важным явное указание параметров
verifyJWT.
Безопасная схема должна включать несколько слоёв:
alg из токенаverifyJWT, а не
decodeПример:
function verify(token, key) {
const header = KJUR.jws.JWS.parse(token).headerObj;
if (header.alg === 'none') {
throw new Error('Запрещённый алгоритм');
}
return KJUR.jws.JWS.verifyJWT(token, key, {
alg: ['RS256']
});
}
decodeJWT вместо
verifyJWTalgnone «для тестов» в продакшенеJWT не должен рассматриваться как доверенный объект до момента полной криптографической проверки.
Любые поля, включая:
algtypkidдолжны рассматриваться как потенциально поддельные.
Корректная реализация всегда предполагает:
none на уровне конфигурацииТакой подход исключает класс уязвимостей, связанных с подменой алгоритма и отсутствием подписи.