JWT токены Декодирование, проверка и избегание распространенных ошибок

Опубликовано
JWT-токены: декодируйте, проверяйте и избегайте распространённых ошибок 1
Реклама · УДАЛИТЬ?
JWT-токены: декодируйте, проверяйте и избегайте распространённых ошибок

JWT-токены присутствуют практически в каждом современном веб-приложении — в заголовках аутентификации, процессах обновления, контроле доступа к API. Они также являются одним из самых часто неправильно используемых стандартов. Если вы работаете с ними, понимание того, что содержится внутри токена, различие между декодированием и проверкой, а также то, какие упрощения могут сломать безопасность, является обязательным.

Анатомия JWT

JWT — это три строки, закодированные в base64url, соединённые точками:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMyIsImVtYWlsIjoiYWxpY2VAZXhhbXBsZS5jb20iLCJpYXQiOjE3MTI3NjQ4MDAsImV4cCI6MTcxMjg1MTIwMH0.4Xr8mNkZQWpH2TvL9uY3sKdJwFqBzEcAoMnRiVePxlU
  • Заголовок — алгоритм и тип токена (alg, typ)
  • Полезная нагрузка — утверждения (данные, которые действительно нужны вашему приложению)
  • Подпись — подпись HMAC или RSA, рассчитанная над первыми двумя частями

После декодирования, содержимое пакета выглядит так:

{
  "sub": "user_123",
  "email": "alice@example.com",
  "iat": 1712764800,
  "exp": 1712851200
}

Здесь ничего не зашифровано. Любой, кто обладает токеном, может прочитать эти значения. Подпись подтверждает, что токен не был изменён после выпуска, но не скрывает содержимое.

Декодирование — это не проверка

Этот аспект часто вызывает ошибки у разработчиков.

Декодирование разделяет токен на части по точкам и декодирует каждую часть в формате base64url. Ключа не требуется — любая программа или одна строка может выполнить это. Если вы хотите декодировать JWT онлайн без установки чего-либо, вставьте токен в IO Tools декодер JWT и мгновенно получите разбор заголовка, пакета и срока действия.

Проверка проверяет, что подпись корректна, используя секрет (или публичный ключ для асимметричных алгоритмов). Также подтверждается, что токен не истёк и что утверждения соответствуют ожидаемым значениям в приложении. Пропуск проверки и доверие декодированному токену — это то, как происходит обход аутентификации.

Вот пример кода на Node.js, который проверяет JWT и обрабатывает распространённые крайние случаи:

import jwt from 'jsonwebtoken';

const SECRET = process.env.JWT_SECRET;

function verifyToken(token) {
  try {
    const payload = jwt.verify(token, SECRET, {
      algorithms: ['HS256'],  // whitelist — never allow 'none'
      audience: 'myapp',      // validate aud claim
    });

    // jwt.verify throws if exp is in the past, but be explicit:
    if (payload.exp < Math.floor(Date.now() / 1000)) {
      throw new Error('Token expired');
    }

    return payload;
  } catch (err) {
    // Never silently swallow verification failures
    throw new Error(`Invalid token: ${err.message}`);
  }
}

Утверждения, которые стоит проверять

Спецификация JWT определяет стандартные утверждения, которые сервер должен активно проверять, а не просто читать:

ClaimЗначениеПроверять?
expВремя истеченияВсегда — устаревшие токены представляют реальный вектор атаки
iatВремя созданияОпционально, полезно для проверки max-age
subПользователь (обычно ID пользователя)Да — убедитесь, что он соответствует ожидаемому пользователю
audНазначенный аудиторДа — предотвращает использование токенов для сервиса A на сервисе B

Большинство библиотек JWT автоматически проверяют exp — но только если вы настроили их на это. Прочитайте документацию по вашей библиотеке. Не предполагайте, что это включено по умолчанию.

Три ошибки, которые приводят к проблемам у разработчиков

1. alg: none Атака

Спецификация JWT допускает значение алгоритма none, что означает отсутствие подписи. Некоторые библиотеки, особенно старые, принимают это и полностью пропускают проверку подписи. Атакующий удаляет подпись, устанавливает "alg": "none" в заголовке и подделывает любые утверждения. Сервер доверяет этому.

Решение: явно перечислите разрешённые алгоритмы при проверке. Никогда не принимайте none. В примере выше это демонстрируется с algorithms: ['HS256'].

2. Не проверка истечения

Декодирование токена и доверие его содержимому без проверки exp означает, что токен, выданный месяц назад, всё ещё будет приниматься. Если сессия пользователя была отменена или злоумышленник получил старый токен, ваше приложение никогда не узнает об этом.

Решение: считайте истечение обязательным, а не опциональным. Чтобы проверить, когда конкретный токен истечёт, IO Tools проверщик истечения JWT декодирует утверждение exp и сообщает, сколько времени осталось — полезно для отладки потоков обновления без написания кода.

3. Хранение JWT в localStorage

localStorage доступен любому JavaScript на странице. Один случай XSS позволяет атакующему безвозвратно извлечь аутентификационный токен. Вот как сравнение вариантов хранения:

ХранениеРиск XSSРиск CSRFДоступно из JavaScriptПримечания
localStorageВысокийНиктоДаНе рекомендуется для хранения аутентификационных токенов
sessionStorageВысокийНиктоДаТе же риски, что и у localStorage
httpOnly cookieНиктоСерединаНетЛучший вариант для аутентификации; используйте вместе с SameSite и токеном CSRF
В памяти (переменная JS)НизкийНиктоДа (в одном контексте)Потеряется при перезагрузке; подходит для кратковременных токенов

HttpOnly куки вообще не могут быть прочитаны JavaScript, что полностью устраняет вектор атаки XSS. Стоимость — риск CSRF, который вы решаете с помощью SameSite=Strict или токена CSRF.

JWT и сессии: честный компромисс

JWT — это безсостоянный механизм — сервер проверяет их без обращения к базе данных. Это удобно в распределённых системах, где вы не хотите, чтобы каждый сервис обращался к общему хранилищу сессий.

Но безсостоянный механизм имеет реальные затраты: вы не можете отменить JWT до истечения. Если пользователь выходит из системы или подвергается компрометации, токен остаётся действительным до exp. Существуют обходные пути (списки заблокированных токенов, короткий срок действия и токены обновления), но они добавляют сложность и часто воспроизводят то, что уже делает хранилище сессий.

Используйте JWT, когда: у вас есть несколько сервисов, которые независимо аутентифицируют запросы, вы хотите встроить роли/разрешения прямо в токен, или ваши токены кратковременные и задержка отмены допустима.

Используйте сессии, когда: нужна немедленная отмена (выход должен работать), вы создаёте приложение, рендеримое на сервере, или простота превосходит преимущества безсостоянного масштабирования.

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

Нужно увидеть, что внутри JWT сейчас? Терминал:

echo "YOUR.JWT.HERE" | cut -d. -f2 | base64 -d 2>/dev/null | python3 -m json.tool

Или пропустите терминал — вставьте токен в IO Tools декодер JWT чтобы получить отформатированную структуру заголовка и пакета. Ни один из этих подходов не проверяет подпись; они просто декодируют. Для быстрого определения срока действия без написания кода, проверщик истечения JWT покажет точное время и оставшееся время.

Хотите убрать рекламу? Откажитесь от рекламы сегодня

Установите наши расширения

Добавьте инструменты ввода-вывода в свой любимый браузер для мгновенного доступа и более быстрого поиска

в Расширение Chrome в Расширение края в Расширение Firefox в Расширение Opera

Табло результатов прибыло!

Табло результатов — это интересный способ следить за вашими играми, все данные хранятся в вашем браузере. Скоро появятся новые функции!

Реклама · УДАЛИТЬ?
Реклама · УДАЛИТЬ?
Реклама · УДАЛИТЬ?

новости с техническими моментами

Примите участие

Помогите нам продолжать предоставлять ценные бесплатные инструменты

Купи мне кофе
Реклама · УДАЛИТЬ?