Отзыв токенов: blocklist и альтернативы

Отзыв токенов в системах, использующих JSON Web Token (JWT), остаётся одной из наиболее сложных задач из-за статeless-природы токенов. После выдачи JWT сервер не хранит состояние, а вся информация о сессии содержится внутри самого токена. Это упрощает масштабирование, но делает невозможным прямую инвалидизацию без дополнительных механизмов.

Библиотека jose предоставляет инструменты для подписи, проверки и шифрования JWT, JWS и JWE, однако механизм отзыва токенов не входит в её область ответственности. Реализация контроля актуальности токенов строится на уровне архитектуры приложения.

JWT после выпуска остаётся валидным до истечения срока действия (exp), даже если пользователь уже вышел из системы или токен скомпрометирован. Это создаёт фундаментальное ограничение:

  • отсутствие серверного состояния не позволяет “удалить” токен
  • проверка подписи гарантирует подлинность, но не актуальность
  • компрометация токена делает его действительным до конца срока жизни

Из этого вытекает необходимость внешних механизмов контроля.

Blocklist (denylist) токенов

Один из наиболее распространённых подходов — использование блоклиста (blacklist/denylist). Суть заключается в хранении списка токенов, которые более не считаются валидными.

Принцип работы

При выдаче JWT в токен добавляется уникальный идентификатор:

  • jti (JWT ID) — уникальная строка
  • либо комбинация sub + iat + exp

При отзыве токена его jti заносится в хранилище блокировки.

Во время каждой проверки токена выполняется дополнительный шаг:

  1. Проверка подписи через jose
  2. Проверка срока действия (exp)
  3. Проверка отсутствия jti в blocklist

Пример структуры токена

{
  "sub": "user_123",
  "iat": 1710000000,
  "exp": 1710003600,
  "jti": "c2f1a9e0-3b2f-4d9a-8c6a-9d1b5e8a7f44"
}

Хранение blocklist

На практике используются:

  • Redis (наиболее распространённый вариант)
  • Memcached (реже из-за отсутствия устойчивости)
  • SQL/NoSQL базы данных

Redis применяется чаще всего из-за TTL-логики: запись автоматически удаляется после exp.

Key: blacklist:c2f1a9e0-3b2f-4d9a-8c6a-9d1b5e8a7f44
Value: revoked
TTL: 3600 секунд

Проверка токена

Логика проверки дополняется запросом:

  • если jti найден в Redis → токен недействителен
  • если отсутствует → проверяется дальше

Ограничения blocklist-подхода

Несмотря на простоту, модель имеет существенные недостатки:

Рост хранилища

При большом количестве пользователей и коротких токенах:

  • увеличивается число записей
  • возрастает нагрузка на Redis/БД
  • требуется строгая TTL-стратегия

Дополнительная задержка

Каждая проверка токена становится не полностью локальной:

  • добавляется сетевой вызов к хранилищу
  • увеличивается latency

Проблема распределённых систем

В микросервисной архитектуре необходимо:

  • синхронизировать blocklist между сервисами
  • обеспечивать консистентность данных

Альтернатива: короткоживущие access-токены

Один из наиболее практичных подходов — сокращение времени жизни JWT до минимального значения (например, 5–15 минут).

Идея

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

Дополнение refresh-токенами

Используется связка:

  • access token — короткий срок жизни
  • refresh token — долгоживущий и контролируемый

Refresh-токен хранится на сервере и может быть инвалидирован.

Архитектура:

  1. Access token используется для API-запросов
  2. По истечении срока выполняется запрос refresh
  3. Refresh token проверяется в базе
  4. При отзыве удаляется refresh token

Этот подход минимизирует необходимость blocklist.

Token rotation (ротация токенов)

Механизм ротации refresh-токенов усиливает безопасность.

Принцип

  • каждый refresh-запрос выдаёт новый refresh token
  • старый становится недействительным

Это позволяет:

  • выявлять повторное использование токена
  • автоматически отзывать скомпрометированные сессии

Whitelist вместо blacklist

Альтернативная модель — использование whitelist (allowlist).

Суть

В системе хранится список активных токенов или сессий:

  • только токены из списка считаются действительными
  • всё остальное автоматически отклоняется

Реализация

Чаще используется не сам JWT, а:

  • session id
  • refresh token id
  • server-side session store

Преимущества

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

Недостатки

  • потеря stateless-преимущества JWT
  • необходимость постоянного обращения к хранилищу

Introspection endpoint (гибридный подход)

В некоторых системах применяется модель introspection:

  • JWT используется как bearer token
  • каждый запрос проверяется через сервер авторизации

Сервер:

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

Этот подход характерен для OAuth2-архитектур.

Использование jti в архитектуре revocation

Поле jti становится центральным элементом при реализации отзывов.

Типовые сценарии:

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

При любом событии:

  • все активные jti пользователя добавляются в blocklist
  • либо удаляются из whitelist

Практика использования jose в контексте ревокации

Библиотека jose используется только для криптографической части:

  • jwtVerify — проверка подписи и структуры
  • SignJWT — выпуск токенов
  • decodeJwt — чтение payload без проверки

Пример проверки с дополнительной логикой:

import { jwtVerify } from 'jose'

async function verifyToken(token, secret, redisClient) {
  const { payload } = await jwtVerify(token, secret)

  const jti = payload.jti
  if (await redisClient.get(`blacklist:${jti}`)) {
    throw new Error('Token revoked')
  }

  return payload
}

Здесь видно, что ревокация реализуется вне библиотеки.

Комбинированные стратегии

На практике используется комбинация методов:

  • короткий TTL access token
  • refresh token с серверным хранением
  • blocklist для критических событий
  • rotation refresh токенов
  • jti для точечной блокировки

Такой подход снижает нагрузку на хранилище и минимизирует риск использования отозванных токенов.

Архитектурные компромиссы

Выбор стратегии зависит от требований:

Высоконагруженные системы

  • предпочтение коротким JWT
  • минимальное использование blocklist
  • Redis как вспомогательный слой

Финансовые и критические системы

  • обязательный whitelist или introspection
  • строгий контроль сессий
  • мгновенный отзыв токенов

Микросервисы

  • централизованный auth-сервис
  • единое хранилище сессий
  • единый слой проверки

Производственные практики

  • TTL должен соответствовать жизненному циклу токена
  • blocklist должен быть минимальным по объёму
  • jti должен генерироваться как UUID
  • refresh token всегда хранится серверной стороной
  • проверка revocation должна быть обязательной частью middleware

Ошибки реализации

Наиболее частые проблемы:

  • отсутствие jti в токенах
  • хранение blocklist без TTL
  • использование JWT как полноценной сессии
  • отсутствие rotation refresh токенов
  • проверка только подписи без проверки статуса

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