Параметр b64 и неприкреплённый payload

В стандарте JSON Web Signature (JWS), описанном в RFC 7515, полезная нагрузка (payload) по умолчанию кодируется в формате Base64URL перед вычислением подписи. Это поведение закреплено как стандартное и ожидаемое большинством реализаций.

Однако расширение RFC 7797 вводит параметр заголовка b64, позволяющий отключить это кодирование. Это открывает возможность работы с неприкреплённым (unencoded) payload, что особенно важно для сценариев с потоковыми данными, большими файлами или уже закодированными структурами.

Значение и поведение b64

Параметр b64 — это булево значение в заголовке JWS:

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

Ключевые особенности:

  • b64: true (по умолчанию) — payload кодируется в Base64URL
  • b64: false — payload используется как есть, без кодирования
  • параметр обязан быть указан в crit, иначе токен считается некорректным

Почему нужен crit: Поле crit (critical headers) сообщает валидатору, что он обязан понимать указанные параметры. Если валидатор не знает про b64, он должен отклонить токен.


Формирование подписи при b64: false

Обычный JWS:

BASE64URL(header) + "." + BASE64URL(payload)

С b64: false:

BASE64URL(header) + "." + payload (в исходном виде)

Это означает:

  • payload не проходит через Base64URL
  • точка (.) остаётся разделителем
  • payload должен быть безопасен для включения в строку (или передаваться отдельно)

Ограничения и требования

Использование b64: false накладывает строгие ограничения:

1. Payload не должен содержать .

Поскольку . используется как разделитель, его наличие в payload нарушает структуру JWS Compact Serialization.

2. Payload должен быть ASCII-совместимым

Некодированные бинарные данные могут привести к некорректной интерпретации. В таких случаях используют Detached Payload.

3. Поддержка на стороне валидатора

Если библиотека не поддерживает b64: false, проверка подписи завершится ошибкой.


Detached Payload (неприкреплённая нагрузка)

Detached Payload — это вариант JWS, при котором payload не включается в сам токен, а передаётся отдельно.

Формат Compact Serialization:

BASE64URL(header) + ".." + BASE64URL(signature)

Обратите внимание на двойную точку .. — это индикатор отсутствующего payload.

Применение

  • передача больших файлов (например, PDF, видео)
  • потоковые данные (streaming)
  • интеграция с HTTP-подписями
  • цифровая подпись внешнего контента

Работа с jose в JavaScript

Библиотека jose (npm пакет) поддерживает b64: false и detached payload через низкоуровневые API.

Подпись с отключённым Base64

import { CompactSign } from 'jose'

const payload = new TextEncoder().encode('example payload')

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

Важно:

  • payload передаётся как Uint8Array
  • b64: false требует явного указания crit

Подпись с Detached Payload

import { FlattenedSign } from 'jose'

const payload = new TextEncoder().encode('external data')

const jws = await new FlattenedSign(payload)
  .setProtectedHeader({
    alg: 'RS256',
    b64: false,
    crit: ['b64']
  })
  .sign(privateKey)

Результат:

{
  "protected": "...",
  "signature": "..."
}

Payload отсутствует — его необходимо передать отдельно при проверке.


Верификация Detached Payload

import { flattenedVerify } from 'jose'

await flattenedVerify(jws, publicKey, {
  payload: externalPayload
})

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

  • payload передаётся явно
  • без него проверка невозможна
  • байтовое совпадение обязательно

Безопасность и риски

Нарушение совместимости

Большинство JWT/JWS библиотек не поддерживают b64: false, особенно в high-level API.

Подмена payload

Если payload передаётся отдельно, важно гарантировать:

  • неизменность данных
  • корректную привязку к подписи
  • отсутствие атак типа replay

Канал передачи

Detached payload должен передаваться по тому же доверенному каналу, что и подпись.


Когда использовать b64: false

Подход оправдан в следующих случаях:

  • необходимость избежать двойного кодирования
  • работа с уже сериализованными форматами (например, XML, JSON)
  • подпись HTTP-запросов (например, body)
  • минимизация накладных расходов

Когда не использовать

  • стандартные JWT (аутентификация, авторизация)
  • межсервисное взаимодействие без строгого контроля формата
  • публичные API без гарантии поддержки crit

Сравнение: обычный JWS vs b64: false

Характеристика Обычный JWS b64: false
Кодирование payload Base64URL Нет
Совместимость Высокая Ограниченная
Поддержка библиотек Повсеместная Частичная
Возможность detached Нет Да
Размер токена Больше Меньше

Практические рекомендации

  • всегда указывать crit: ['b64']
  • использовать Uint8Array для payload
  • избегать символа . в данных
  • тестировать на совместимость с валидаторами
  • документировать использование нестандартного поведения

Внутренние детали реализации в jose

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

  • проверяет наличие crit
  • валидирует b64 на этапе подписи и верификации
  • требует явной передачи payload при detached режиме
  • не допускает silent fallback на стандартное поведение

Это делает реализацию безопасной, но строгой.


Связь с RFC

  • RFC 7515 — базовый стандарт JWS
  • RFC 7797 — расширение для unencoded payload

Поддержка b64: false — это именно расширение, а не часть базового стандарта.


Особенности сериализаций

Compact Serialization

  • ограничена (payload должен быть inline или отсутствовать)
  • требует осторожности с символами

JSON Serialization

  • более гибкая
  • позволяет явно указать отсутствие payload
  • лучше подходит для detached сценариев

Итоговые замечания по архитектуре

Использование b64: false — это переход от стандартной модели JWT к более низкоуровневой криптографической подписи. Это даёт гибкость, но требует строгого контроля:

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

В большинстве прикладных задач достаточно стандартного Base64URL-кодирования, а b64: false остаётся инструментом для специализированных сценариев, где важна эффективность и контроль над байтовым представлением данных.