Аутентификация с JSON Web Tokens (JWT) стала стандартом для современных веб-приложений благодаря своей простоте и отсутствию необходимости хранить состояние на сервере. Однако за кажущейся элементарностью скрываются подводные камни, которые могут свести на нет все преимущества технологии. Рассмотрим практические аспекты безопасной реализации JWT-аутентификации через призму реальных инцидентов безопасности.
Ошибка 1: Хранение токена в localStorage
Популярный антипаттерн — сохранение JWT в localStorage
для удобства доступа из JavaScript:
// Опасный подход
localStorage.setItem('jwt', token);
Проблема: XSS-атаки позволяют злоумышленнику извлечь токен через инъекцию скриптов. В одном проекте 2022 года это привело к утечке 150 тыс. пользовательских сессий.
Решение: Используйте HttpOnly
куки с флагом Secure
:
// Безопасная установка куки
res.cookie('token', token, {
httpOnly: true,
secure: true,
sameSite: 'Strict',
maxAge: 900000 // 15 минут
});
Это предотвращает чтение токена через JavaScript, но требует защиты от CSRF через токены синхронизации (Synchronizer Token Pattern).
Ошибка 2: Слепое доверие заголовку alg
Рассмотрим типичную проверку токена в Node.js:
jwt.verify(token, publicKey, { algorithms: ['RS256'] });
Если не задать явный список алгоритмов, злоумышленник может подменить alg: "none"
и обойти проверку подписи. В 2021 году подобная уязвимость была обнаружена в 23% проанализированных open-source проектов.
Исправление:
- Жёстко задавайте допустимые алгоритмы
- Всегда проверяйте соответствие алгоритма и типа ключа
const decoded = jwt.verify(token, (header, callback) => {
if (header.alg !== 'RS256') return callback(new Error('Invalid algorithm'));
callback(null, publicKey);
});
Ошибка 3: Вечные токены и "ленивый" refresh
Типичный сценарий с долгоживущими access-токенами (например, 24 часа) — бомба замедленного действия. При утечке токен остаётся валидным до истечения срока.
Архитектурное решение:
- Access-токены с TTL 5-15 минут
- Refresh-токены с умеренным TTL (7 дней)
- Механизм отзыва refresh-токенов
Пример Redis-хранилища для отозванных токенов:
async function revokeToken(jti) {
await redis.set(`jwt:${jti}`, 'revoked', 'EX', 3600 * 24 * 7);
}
async function verifyToken(token) {
const decoded = jwt.verify(token, publicKey);
if (await redis.exists(`jwt:${decoded.jti}`)) {
throw new Error('Token revoked');
}
return decoded;
}
Ошибка 4: Токен как хранилище данных
Разработчики часто злоупотребляют полем payload
, добавляя:
{
"user": {
"email": "admin@example.com",
"password_hash": "$2a$12$..."
}
}
Риски: Base64-декодирование токена даёт доступ к потенциально чувствительным данным даже без проверки подписи.
Рекомендация:
- Хранить только идентификатор пользователя (
sub
) - Добавлять JWT ID (
jti
) для отслеживания токенов - Включать scope доступа (
roles
,permissions
) только при необходимости
Ошибка 5: Статический секрет для подписи
Жёстко заданный секрет в коде — частая причина взломов:
const SECRET = 'my_super_secret'; // Уязвимость!
Решение:
- Динамически генерируемые секреты с использованием KMS (Key Management Service)
- Регулярная ротация ключей
- Для RSA использовать разные ключи для подписи и верификации
Пример ротации ключей в Kubernetes:
apiVersion: v1
kind: Secret
metadata:
name: jwt-keys
type: Opaque
data:
current_priv.key: |
LS0tLS1CRUdJTiBSU0EgUF...
current_pub.key: |
LS0tLS1CRUdJTiBQVUJM...
previous_pub.key: |
LS0tLS1CRUdJTiBQVUJM...
Заключение: безопасность как процесс
JWT — не серебряная пуля для аутентификации. Его эффективность зависит от:
- Правильной имплементации криптографических примитивов
- Защиты каналов передачи (HTTPS/HSTS)
- Системы мониторинга подозрительной активности
- Регулярного аудита зависимостей (например, уязвимостей в библиотеке
jsonwebtoken
)
Перед запуском в продакшен проверьте:
- Все токены имеют JTI (JWT ID)
- Реализован механизм отзыва через blacklist/whitelist
- Алгоритмы подписи явно заданы и проверяются
- Секреты и приватные ключи не хранятся в репозитории
Технический долг в системе аутентификации подобен трещинам в фундаменте здания — незаметен до первого серьёзного сотрясения. Инвестиции в безопасность J-токенов на этапе проектирования избавят от катастрофических последствий в будущем.