Проверка JWT с помощью KJUR.jws.JWS.verify

JWT представляет собой компактный токен, состоящий из трёх частей: заголовка, полезной нагрузки и подписи. В библиотеке jsrsasign проверка целостности и подлинности такого токена выполняется через низкоуровневый механизм работы с JWS (JSON Web Signature), где основная роль принадлежит методу KJUR.jws.JWS.verify.

JWT фактически является частным случаем JWS. Токен имеет вид:

header.payload.signature

Каждая часть закодирована в Base64URL. Подпись вычисляется на основе первых двух частей и секретного ключа (для HMAC) или приватного ключа (для RSA/ECDSA).

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

Основной метод проверки подписи

В jsrsasign проверка JWS реализуется через:

KJUR.jws.JWS.verify(jws, key, alg)

Параметры:

  • jws — строка JWT/JWS
  • key — секретный ключ (для HS256) или публичный ключ в PEM-формате (для RS256/ES256)
  • alg — алгоритм подписи (например, "HS256", "RS256")

Метод возвращает:

  • true — подпись корректна
  • false — подпись недействительна или данные повреждены

Пример проверки HS256

HS256 использует симметричный ключ, который одинаков для подписи и проверки:

const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
const secret = "my_secret_key"

const isValid = KJUR.jws.JWS.verify(jwt, secret, ["HS256"])

Важно, что алгоритм передаётся как массив допустимых значений. Это позволяет ограничить список разрешённых алгоритмов.

Проверка RS256 с публичным ключом

В случае RSA используется асимметричная криптография. Подпись создаётся приватным ключом, а проверка выполняется публичным:

const jwt = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
const publicKey = `
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqh...
-----END PUBLIC KEY-----
`

const isValid = KJUR.jws.JWS.verify(jwt, publicKey, ["RS256"])

Ключ должен быть в корректном PEM-формате, иначе проверка завершится ошибкой.

Разбор процесса проверки внутри verify

Метод выполняет несколько последовательных шагов:

  1. Разделение JWT на три части
  2. Декодирование header и payload
  3. Извлечение алгоритма из header
  4. Проверка, входит ли алгоритм в список допустимых
  5. Пересчёт подписи на основе header.payload
  6. Сравнение вычисленной подписи с переданной

Если хотя бы один этап не проходит проверку, результат будет false.

Ограничение допустимых алгоритмов

Передача массива алгоритмов — важный механизм защиты:

KJUR.jws.JWS.verify(jwt, key, ["RS256", "RS512"])

Это предотвращает атаки, связанные с подменой алгоритма (например, downgrade attack, когда злоумышленник пытается заменить RS256 на HS256).

Проверка JWT с дополнительными параметрами

Хотя KJUR.jws.JWS.verify проверяет только подпись, на практике JWT требует дополнительной валидации:

  • exp — срок действия
  • nbf — начало действия
  • iss — издатель
  • aud — аудитория

Эти проверки выполняются отдельно после декодирования payload:

const payloadObj = KJUR.jws.JWS.parse(jwt).payloadObj

const now = Math.floor(Date.now() / 1000)

if (payloadObj.exp < now) {
  throw new Error("Токен истёк")
}

Отличие от verifyJWT

В библиотеке также существует более высокий уровень:

KJUR.jws.JWS.verifyJWT()

Но verify используется чаще в низкоуровневых сценариях, где требуется:

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

Типичные ошибки при проверке

Частые причины возврата false:

  • неверный формат ключа
  • несоответствие алгоритма
  • повреждённый токен (изменён payload или signature)
  • использование приватного ключа вместо публичного при RS256
  • лишние пробелы или переносы строк в JWT

Работа с несколькими алгоритмами

Иногда система поддерживает несколько схем подписи одновременно:

const isValid = KJUR.jws.JWS.verify(
  jwt,
  publicKey,
  ["RS256", "RS384", "RS512"]
)

Это полезно при миграции криптографических алгоритмов или интеграции с внешними сервисами.

Безопасные практики использования verify

При использовании проверки подписи важно учитывать следующие моменты:

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

Разбор результата подписи

Если проверка успешна, это означает только одно — токен не был изменён после подписания. Это не гарантирует:

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

Поэтому KJUR.jws.JWS.verify используется как первый уровень проверки, после которого всегда следует анализ payload.

Пример полной цепочки проверки

const jwt = "..."

const isValidSignature = KJUR.jws.JWS.verify(jwt, publicKey, ["RS256"])

if (!isValidSignature) {
  throw new Error("Некорректная подпись")
}

const parsed = KJUR.jws.JWS.parse(jwt)
const payload = parsed.payloadObj

const now = Math.floor(Date.now() / 1000)

if (payload.exp <= now) {
  throw new Error("JWT просрочен")
}

Такая схема разделяет криптографическую проверку и бизнес-валидацию, что делает систему более предсказуемой и безопасной.