Заголовки JWS: alg, kid, jwk, x5c, crit, b64

Заголовок JWS (JSON Web Signature) — это JSON-объект, содержащий метаданные, описывающие способ подписи и дополнительные параметры, влияющие на обработку токена. Заголовок кодируется в Base64URL и размещается в первой части JWS.

Ключевые параметры заголовка позволяют определить:

  • алгоритм подписи
  • источник ключа
  • особенности обработки полезной нагрузки

Ниже рассмотрены основные поля: alg, kid, jwk, x5c, crit, b64.


alg — алгоритм подписи

Поле alg (algorithm) является обязательным и указывает, какой криптографический алгоритм используется для подписи.

Примеры значений:

  • HS256 — HMAC с SHA-256
  • RS256 — RSA с SHA-256
  • ES256 — ECDSA с SHA-256
  • EdDSA — Ed25519 / Ed448

Особенности:

  • Определяет как создавать и проверять подпись
  • Должен строго совпадать на стороне проверки
  • Значение none означает отсутствие подписи (используется редко и требует особой осторожности)

Пример:

{
  "alg": "RS256"
}

В библиотеке jose:

import { SignJWT } from 'jose'

const jwt = await new SignJWT({ sub: '123' })
  .setProtectedHeader({ alg: 'RS256' })
  .sign(privateKey)

kid — идентификатор ключа

Поле kid (key ID) используется для указания конкретного ключа, которым была создана подпись.

Назначение:

  • Позволяет выбрать нужный ключ из набора (JWKS)
  • Ускоряет поиск ключа при проверке подписи
  • Особенно полезен при ротации ключей

Пример:

{
  "alg": "RS256",
  "kid": "key-2024-01"
}

Типичный сценарий:

  • сервер публикует несколько публичных ключей
  • токен содержит kid
  • валидатор выбирает соответствующий ключ

В jose:

.setProtectedHeader({
  alg: 'RS256',
  kid: 'my-key-id'
})

jwk — встроенный JSON Web Key

Поле jwk содержит публичный ключ прямо в заголовке JWS.

Структура:

  • представляет собой объект JWK
  • включает параметры ключа (kty, n, e, crv и т.д.)

Пример:

{
  "alg": "RS256",
  "jwk": {
    "kty": "RSA",
    "n": "...",
    "e": "AQAB"
  }
}

Особенности:

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

Ограничения:

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

В jose:

.setProtectedHeader({
  alg: 'RS256',
  jwk: publicJwk
})

x5c — цепочка сертификатов X.509

Поле x5c содержит массив сертификатов X.509 в формате Base64.

Назначение:

  • подтверждение подлинности ключа через PKI
  • предоставление цепочки доверия

Пример:

{
  "alg": "RS256",
  "x5c": [
    "MIIDdzCCAl+gAwIBAgIEb1...",
    "MIID...root"
  ]
}

Особенности:

  • первый элемент — сертификат подписывающего ключа
  • последующие — промежуточные сертификаты
  • позволяет валидатору проверить цепочку доверия

Когда используется:

  • интеграции с корпоративной PKI
  • OAuth/OpenID провайдеры
  • банковские API

В jose:

.setProtectedHeader({
  alg: 'RS256',
  x5c: certChainArray
})

crit — критические параметры

Поле crit (critical) — массив строк, указывающих на заголовочные параметры, которые должны быть обязательно понятны обработчику.

Пример:

{
  "alg": "RS256",
  "crit": ["b64"],
  "b64": false
}

Смысл:

  • если обработчик не понимает указанные поля — он должен отклонить токен
  • предотвращает игнорирование важных параметров

Особенности:

  • используется для расширения стандарта
  • повышает безопасность при добавлении нестандартных полей

Важно:

  • каждое поле из crit должно присутствовать в заголовке
  • библиотека должна явно поддерживать эти поля

В jose:

.setProtectedHeader({
  alg: 'RS256',
  crit: ['b64'],
  b64: false
})

b64 — управление кодированием payload

Поле b64 определяет, будет ли полезная нагрузка (payload) закодирована в Base64URL.

Значения:

  • true (по умолчанию) — payload кодируется
  • false — payload передается в “сыром” виде

Пример:

{
  "alg": "RS256",
  "b64": false,
  "crit": ["b64"]
}

Особенности:

  • обязательно указывается в crit, если используется
  • позволяет подписывать уже закодированные или бинарные данные
  • используется в HTTP Signatures и streaming-сценариях

Плюсы:

  • избегает двойного кодирования
  • уменьшает размер данных

Минусы:

  • усложняет обработку
  • требует поддержки со стороны всех участников

В jose:

import { CompactSign } from 'jose'

const encoder = new TextEncoder()

const jws = await new CompactSign(encoder.encode('payload'))
  .setProtectedHeader({
    alg: 'HS256',
    b64: false,
    crit: ['b64']
  })
  .sign(secret)

Взаимодействие заголовков

Заголовки часто используются совместно:

Пример комплексного заголовка:

{
  "alg": "RS256",
  "kid": "key-1",
  "x5c": ["..."],
  "crit": ["b64"],
  "b64": false
}

Типичные комбинации:

  • alg + kid — стандартный сценарий с JWKS
  • alg + jwk — самодостаточный токен
  • alg + x5c — проверка через сертификаты
  • crit + нестандартные поля — расширения протокола

Практические аспекты безопасности

  • Несоответствие alg и реального алгоритма — частая уязвимость
  • Игнорирование crit может привести к обходу проверки
  • Использование jwk требует проверки доверия к источнику
  • x5c требует полноценной валидации цепочки сертификатов

Рекомендации:

  • всегда явно проверять alg
  • использовать kid при наличии нескольких ключей
  • избегать none, если это не строго контролируемая среда
  • обрабатывать crit строго по спецификации

Поддержка в библиотеке jose

Библиотека jose:

  • автоматически валидирует alg
  • поддерживает kid при работе с JWKS
  • позволяет задавать произвольные заголовки
  • корректно обрабатывает crit и b64 при явном указании

Пример проверки JWS:

import { jwtVerify } from 'jose'

const { payload, protectedHeader } = await jwtVerify(token, publicKey)

console.log(protectedHeader.alg)
console.log(protectedHeader.kid)

Итоговая структура заголовка

Заголовок JWS — это гибкий механизм управления криптографией и метаданными:

  • alg определяет криптографию
  • kid, jwk, x5c — способы указания ключа
  • crit — механизм строгой проверки расширений
  • b64 — управление кодированием данных

Корректное использование этих параметров позволяет строить безопасные, расширяемые и совместимые системы аутентификации и подписи данных.