Критические заголовки: параметр crit

В спецификациях семейства JOSE (JSON Object Signing and Encryption) предусмотрен механизм расширяемости заголовков. Одним из ключевых инструментов контроля совместимости и безопасности является параметр crit (critical). Он определяет список заголовочных параметров, которые получатель обязан понимать и корректно обрабатывать. В противном случае обработка токена должна быть немедленно прекращена.

Семантика и требования спецификации

Параметр crit представляет собой массив строк, каждая из которых соответствует имени заголовочного параметра. Основные правила:

  • Все параметры, перечисленные в crit, обязательны к пониманию.
  • Если получатель не поддерживает хотя бы один из них — токен считается недействительным.
  • Параметры из crit должны присутствовать в заголовке.
  • Сам crit не может включать стандартные параметры, которые и так определены спецификацией как обязательные для понимания (например, alg).

Пример заголовка JWS:

{
  "alg": "HS256",
  "crit": ["exp", "custom"],
  "exp": 1710000000,
  "custom": "value"
}

В этом случае получатель обязан:

  1. Понимать параметр exp
  2. Понимать параметр custom

Если реализация игнорирует 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 в crit
  • Если в токене окажется другой параметр в crit, например 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, если нет необходимости в расширениях
  • Избегать универсальных обработчиков, игнорирующих специфику параметров

Взаимодействие с JWE

Параметр crit применяется аналогично и в JWE (шифрованные токены). Поведение библиотеки jose идентично:

import { compactDecrypt } from 'jose';

await compactDecrypt(token, key, {
  crit: ['custom'],
});

Требования остаются теми же:

  • Явное разрешение
  • Обязательная обработка

Безопасностный контекст

crit — механизм защиты от несовместимых или неизвестных расширений. Его игнорирование может привести к:

  • некорректной интерпретации токена
  • обходу проверок
  • уязвимостям в логике авторизации

Именно поэтому библиотека jose делает его строго обязательным к декларации и обработке.