В спецификациях семейства JOSE (JSON Object Signing and Encryption)
предусмотрен механизм расширяемости заголовков. Одним из ключевых
инструментов контроля совместимости и безопасности является параметр
crit (critical). Он определяет список заголовочных
параметров, которые получатель обязан понимать и корректно обрабатывать.
В противном случае обработка токена должна быть немедленно
прекращена.
Параметр crit представляет собой массив строк, каждая из
которых соответствует имени заголовочного параметра. Основные
правила:
crit,
обязательны к пониманию.crit должны присутствовать в
заголовке.crit не может включать стандартные параметры,
которые и так определены спецификацией как обязательные для понимания
(например, alg).Пример заголовка JWS:
{
"alg": "HS256",
"crit": ["exp", "custom"],
"exp": 1710000000,
"custom": "value"
}
В этом случае получатель обязан:
expcustomЕсли реализация игнорирует custom, она должна отклонить
токен.
crit в
библиотеке joseБиблиотека jose для JavaScript строго следует
требованиям RFC и по умолчанию отклоняет любые токены с
crit, если явно не указано, какие параметры
считаются допустимыми.
Это сделано для предотвращения атак, связанных с игнорированием критических расширений.
crit при
верификацииПри использовании функций верификации (например,
jwtVerify или compactVerify) необходимо явно
указать список поддерживаемых критических параметров через опцию
crit.
Пример:
import { jwtVerify } from 'jose';
const token = '...';
const { payload, protectedHeader } = await jwtVerify(token, key, {
crit: ['custom'],
});
В этом случае:
custom в
critcrit, например
unknown, произойдёт ошибкаВажно понимать: указание параметра в crit — это не
только разрешение, но и обязательство корректной
обработки.
Пример ошибки проектирования:
await jwtVerify(token, key, {
crit: ['custom'],
});
Если после этого код не использует
protectedHeader.custom, это нарушает смысл
crit. Разрешение без обработки фактически отключает
защитный механизм.
Частое применение crit — внедрение нестандартных
заголовков:
{
"alg": "RS256",
"crit": ["b64"],
"b64": false
}
Этот пример используется в расширении RFC 7797 (Unencoded Payload).
Здесь параметр b64 меняет поведение кодирования
payload.
В библиотеке jose необходимо явно разрешить
b64:
await compactVerify(token, key, {
crit: ['b64'],
});
Без этого вызов завершится ошибкой.
Библиотека jose накладывает дополнительные
ограничения:
crit, если соответствующие
параметры отсутствуютcrit должен быть массивом
строк)Это предотвращает некорректные или вредоносные конструкции.
import { jwtVerify } from 'jose';
const token = '...';
const result = await jwtVerify(token, key, {
crit: ['custom'],
});
const { protectedHeader } = result;
// обязательная обработка
if (protectedHeader.custom !== 'expected') {
throw new Error('Invalid custom header');
}
Если опция crit не указана:
crit в заголовке приведёт к ошибкеИгнорирование значений: Разрешён параметр, но не используется в логике.
Слишком широкое разрешение:
crit: ['custom', 'another', 'test']
При этом реально используется только один — увеличивается поверхность атаки.
Доверие без проверки: Использование параметров без валидации их значений.
crit, если нет необходимости в
расширенияхПараметр crit применяется аналогично и в JWE
(шифрованные токены). Поведение библиотеки jose
идентично:
import { compactDecrypt } from 'jose';
await compactDecrypt(token, key, {
crit: ['custom'],
});
Требования остаются теми же:
crit — механизм защиты от несовместимых или неизвестных
расширений. Его игнорирование может привести к:
Именно поэтому библиотека jose делает его строго
обязательным к декларации и обработке.