Redis и JWT: хранение черного списка токенов
Redis и JWT — два мощных инструмента, которые при правильной интеграции обеспечивают высокую производительность и безопасность веб-приложений. Один из ключевых вызовов при использовании JWT (JSON Web Tokens) — невозможность преждевременной отмены токена из-за его stateless-природы. Решение этой проблемы — хранение черного списка токенов в Redis, что позволяет эффективно управлять сессиями пользователей и мгновенно отзывать доступ.
- Проблема отзыва JWT-токенов
- Почему Redis подходит для хранения черного списка
- Реализация черного списка: пошаговое руководство
- Шаг 1: Генерация JWT с уникальным идентификатором (jti)
- Шаг 2: Выход пользователя (logout)
- Шаг 3: Middleware проверки токена
- Шаг 4: Обработка refresh-токенов
- Оптимизация и лучшие практики
- Используйте короткие TTL для access-токенов
- Храните только jti, а не полный токен
- Группируйте ключи по префиксам
- Мониторинг и логирование
- Резервное копирование (если нужно)
- Типичные ошибки и как их избежать
- Ошибка 1: Отсутствие jti в JWT
- Ошибка 2: Неправильный TTL
- Ошибка 3: Отсутствие проверки на каждом запросе
- Ошибка 4: Хранение токенов в открытом виде
- Ошибка 5: Игнорирование масштабирования
- Экспертное мнение
- Вопросы и ответы
- Заключение
Проблема отзыва JWT-токенов
JWT — стандарт аутентификации, позволяющий передавать утверждения (claims) между сторонами в виде JSON-объекта. Эти токены подписываются сервером и могут быть проверены без обращения к базе данных. Это делает аутентификацию быстрой и масштабируемой, особенно в распределённых системах.
Однако у JWT есть существенный недостаток: токен действителен до истечения срока жизни (exp claim), независимо от того, вышел ли пользователь из системы или его аккаунт был заблокирован. В отличие от сессий на основе cookies, где сервер может удалить запись в любой момент, JWT остаётся валидным, пока не истечёт время.
Это создаёт серьёзную уязвимость. Например, если токен был украден, злоумышленник сможет использовать его до окончания срока действия. Проблема усугубляется при использовании долгоживущих токенов, таких как refresh tokens, срок действия которых может достигать нескольких дней или недель.
Чтобы решить эту проблему, необходимо внедрить механизм отзыва токенов. Один из наиболее эффективных подходов — использование временного хранилища для отозванных токенов, где каждый запрос с токеном проверяется на наличие в «чёрном списке».
Почему Redis подходит для хранения черного списка
Redis — это in-memory key-value хранилище, известное своей скоростью, простотой использования и поддержкой TTL (Time To Live). Именно эти свойства делают его идеальным кандидатом для реализации черного списка JWT-токенов.
Во-первых, Redis работает в оперативной памяти, что обеспечивает миллисекундный доступ к данным. Это критически важно при каждом HTTP-запросе, где нужно проверять токен на наличие в чёрном списке. Даже при нагрузке в тысячи запросов в секунду задержки будут минимальны.
Во-вторых, Redis поддерживает автоматическое удаление ключей по истечении времени. При добавлении токена в чёрный список можно установить TTL, равное оставшемуся времени жизни токена. Это исключает необходимость ручной очистки и предотвращает бесконечный рост хранилища.
В-третьих, Redis легко интегрируется с большинством современных фреймворков и языков программирования. Есть официальные клиенты для Node.js, Python, Go, Java, PHP и других. Это упрощает разработку и снижает порог входа.
Критерий |
Redis |
PostgreSQL |
Файловая система |
|---|---|---|---|
Скорость доступа |
микросекунды |
миллисекунды |
зависит от диска |
Поддержка TTL |
да |
нет (требует триггеров) |
нет |
Масштабируемость |
высокая (кластеры) |
средняя |
низкая |
Лёгкость интеграции |
высокая |
средняя |
низкая |
Реализация черного списка: пошаговое руководство
Реализация отзыва JWT через Redis состоит из нескольких этапов: генерация токена, выход пользователя, проверка токена и управление временем жизни.
Шаг 1: Генерация JWT с уникальным идентификатором (jti)
Каждый JWT должен содержать уникальный идентификатор — jti (JWT ID). Это позволяет однозначно идентифицировать токен при отзыве.
«`json
{
«sub»: «1234567890»,
«name»: «Иван Петров»,
«iat»: 1620000000,
«exp»: 1620003600,
«jti»: «abc123xyz»
}
«`
Используйте криптографически безопасный генератор случайных строк (например, UUID v4) для jti.
Шаг 2: Выход пользователя (logout)
Когда пользователь нажимает «Выйти», отправьте запрос на сервер, который добавляет jti в Redis с TTL, равным оставшемуся времени жизни токена.
На Node.js с использованием `ioredis`:
«`javascript
const redis = new Redis();
app.post(‘/logout’, authenticateJWT, async (req, res) => {
const { jti } = req.user;
const exp = req.user.exp;
const ttl = exp — Math.floor(Date.now() / 1000);
await redis.setex(`blacklist:${jti}`, ttl, ‘true’);
res.json({ message: ‘Выход выполнен’ });
});
«`
Шаг 3: Middleware проверки токена
Перед обработкой каждого защищённого маршрута проверяйте, не находится ли jti токена в чёрном списке.
«`javascript
const checkBlacklist = async (req, res, next) => {
const token = req.headers.authorization?.split(‘ ‘)[1];
if (!token) return next();
try {
const decoded = jwt.verify(token, SECRET);
const isBlacklisted = await redis.get(`blacklist:${decoded.jti}`);
if (isBlacklisted) {
return res.status(401).json({ error: ‘Токен отозван’ });
}
req.user = decoded;
next();
} catch (err) {
return res.status(401).json({ error: ‘Неверный токен’ });
}
};
«`
Шаг 4: Обработка refresh-токенов
Refresh-токены также должны быть отзываемыми. Их можно хранить в Redis с метаданными: user_id, jti, дата создания, устройство.
При отзыве учётной записи или смене пароля удалите все refresh-токены пользователя:
«`sql
— Условный пример: удаление всех токенов пользователя
await redis.del(
…await redis.keys(`refresh:user:${userId}:*`)
);
«`
Оптимизация и лучшие практики
Хотя Redis быстр, при неправильном использовании возможны проблемы с производительностью и безопасностью.
Используйте короткие TTL для access-токенов
Access-токены должны жить недолго — от 15 минут до 1 часа. Это ограничивает окно уязвимости при утечке и уменьшает размер чёрного списка.
Храните только jti, а не полный токен
Ключ в Redis: `blacklist:{jti}`, значение: `’1’`. Это минимизирует потребление памяти.
Группируйте ключи по префиксам
Используйте осмысленные префиксы:
- `blacklist:{jti}` — отозванные access-токены
- `refresh:user:{id}:{device}` — активные refresh-токены
- `rate_limit:{ip}` — ограничение запросов
Это упрощает администрирование и массовые операции.
Мониторинг и логирование
Настройте мониторинг использования памяти в Redis. Используйте команды:
- `INFO memory` — объём используемой памяти
- `KEYS blacklist:*` — количество отозванных токенов (в продакшене лучше использовать `SCAN`)
Автоматизируйте оповещения при превышении порога в 70% от лимита памяти.
Резервное копирование (если нужно)
По умолчанию Redis не сохраняет данные на диск. Если важна отказоустойчивость, включите RDB или AOF. Однако для чёрного списка это часто избыточно — токены всё равно истекут.
Типичные ошибки и как их избежать
Ошибка 1: Отсутствие jti в JWT
Без уникального идентификатора невозможно точно идентифицировать токен. Все токены одного пользователя будут иметь одинаковый набор claims, и отзыв одного повлечёт отзыв всех.
Решение: всегда генерируйте jti при создании токена.
Ошибка 2: Неправильный TTL
Установка TTL меньше времени жизни токена приведёт к тому, что токен будет считаться валидным после истечения срока в Redis. Наоборот — слишком большой TTL расходует память.
Решение: TTL = exp – iat (или оставшееся время при logout).
Ошибка 3: Отсутствие проверки на каждом запросе
Если middleware проверки чёрного списка пропущено для некоторых маршрутов, отозванные токены останутся действительными.
Решение: централизуйте проверку в едином middleware и применяйте ко всем защищённым эндпоинтам.
Ошибка 4: Хранение токенов в открытом виде
Сохранение полного JWT в Redis увеличивает риск утечки данных, особенно если Redis доступен извне.
Решение: храните только jti как ключ, значение может быть любым (например, ‘1’).
Ошибка 5: Игнорирование масштабирования
Одиночный экземпляр Redis становится узким местом. При росте числа пользователей нужна репликация или кластеризация.
Решение: используйте Redis Cluster или managed-сервисы (AWS ElastiCache, Google Cloud Memorystore).
Экспертное мнение
При проектировании системы отзыва токенов важно соблюдать баланс между безопасностью и производительностью. Полный переход к stateful-аутентификации сводит на нет преимущества JWT. Хранение черного списка в Redis — разумный компромисс.
Рассмотрите комбинированный подход: короткие access-токены (15–30 мин) + долгие refresh-токены (7–30 дней) с возможностью отзыва. Refresh-токены хранятся в Redis с возможностью отзыва при выходе, смене пароля или подозрительной активности.
Для критически важных систем добавьте проверку на изменение IP или User-Agent. Если токен используется с нового устройства — запросите повторную аутентификацию.
Не используйте черный список для всех токенов по умолчанию. Реализуйте его как опциональный механизм для случаев, когда требуется немедленный отзыв: выход пользователя, блокировка аккаунта, сброс пароля.
Масштабируйте Redis заранее. Оцените пиковое количество одновременных сессий и выберите подходящий размер инстанса. Для миллионов пользователей используйте кластеризацию и шардирование по user_id.
Вопросы и ответы
Заключение
Интеграция Redis и JWT для управления чёрным списком токенов — это практичное решение, сочетающее преимущества stateless-аутентификации и контроля безопасности. Оно позволяет быстро отзывать доступ, предотвращать использование украденных токенов и строить масштабируемые приложения.
- JWT не поддерживает отзыв по умолчанию — требуется дополнительный механизм.
- Redis идеально подходит для хранения чёрного списка благодаря скорости и TTL.
- Всегда используйте jti для уникальной идентификации токенов.
- Оптимизируйте TTL, структуру ключей и мониторинг использования памяти.
- Комбинируйте короткие access-токены с отзываемыми refresh-токенами для баланса безопасности и удобства.
⚠️ Дисклеймер — нажмите, чтобы развернуть
Материалы, опубликованные в разделе «Блог» на сайте RU DESIGN SHOP (rudesignshop.ru), носят исключительно информационный и ознакомительный характер и не являются руководством к действию, финансовой рекомендацией, медицинской услугой, ветеринарным назначением либо рекламой товаров и услуг, включая азартные игры. Публикации не содержат призывов к участию в азартных играх и не направлены на продвижение соответствующих операторов.
Безопасность применения товаров и веществ: при использовании строительных материалов, бытовой химии, пестицидов и агрохимикатов необходимо строго следовать инструкциям производителя и действующему законодательству Российской Федерации, включая Федеральный закон РФ от 19.07.1997 № 109-ФЗ «О безопасном обращении с пестицидами и агрохимикатами».
Упоминание товарных знаков, брендов и организаций носит исключительно информационный характер и не означает наличие партнёрских отношений или одобрения со стороны правообладателей.
Материалы, содержащие сведения о медицинских, ветеринарных или косметических средствах, представлены в справочных целях и не являются медицинской консультацией или назначением. Перед применением рекомендуется обратиться к врачу, ветеринарному специалисту или иному сертифицированному профессионалу.
Возрастные ограничения: материалы, содержащие сведения о продукции категории 18+, включая алкоголь или азартные игры, предназначены исключительно для совершеннолетней аудитории и публикуются в информационных целях.
Правовая ответственность: решения, принятые на основе опубликованной информации, пользователь принимает самостоятельно и на свой риск; редакция и авторы несут ответственность в пределах, установленных законодательством Российской Федерации.
Редакция не допускает публикаций, содержащих пропаганду экстремизма, терроризма, наркотических средств или суицида; подобные материалы подлежат немедленному удалению.
Упоминание организаций с ограниченным статусом: компания Meta Platforms Inc. (социальные сети Facebook и Instagram) признана экстремистской организацией решением суда РФ, её деятельность запрещена на территории Российской Федерации; любые упоминания приводятся исключительно в информационных целях.
Авторские права и источники: информация собирается из открытых источников; её актуальность указывается на дату публикации и может изменяться.
Изображения и иллюстрации используются на условиях, разрешённых правообладателями. При возникновении претензий редакция готова оперативно рассмотреть обращение и внести необходимые изменения.
Персональные данные и cookies: сайт использует cookies и обрабатывает персональные данные пользователей в соответствии с Федеральным законом № 152-ФЗ «О персональных данных» и Политикой конфиденциальности RU DESIGN SHOP.
Мнения авторов могут не совпадать с позицией государственных органов или коммерческих организаций, упомянутых в материалах.