JWT
2 инструментов
Основы веб-безопасности
Веб-безопасность — это практика защиты веб-приложений и API от несанкционированного доступа, утечек данных и атак. Современные приложения применяют аутентификацию (подтверждение личности), авторизацию (подтверждение прав) и криптографию для обеспечения границ безопасности.
JSON Web Token (JWT) — доминирующий механизм аутентификации без состояния в современной веб-разработке. Понимание того, как работают JWT — их структура, алгоритмы подписи и уязвимости — необходимо для построения безопасных API и приложений.
JSON Web Tokens (JWT)
JWT — это компактный, URL-безопасный способ представления утверждений между двумя сторонами. Он состоит из трёх частей, закодированных в Base64url и разделённых точками. Поскольку подпись охватывает весь токен, JWT можно верифицировать без обращения к базе данных — это делает их идеальными для масштабируемых микросервисных архитектур.
Три части JWT
Содержит метаданные токена: алгоритм подписи (alg) и тип токена (typ). Всегда представляет собой JSON, закодированный в Base64url. Выбор алгоритма напрямую влияет на безопасность всего токена — никогда не принимайте alg из ненадёжного источника.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9{
"alg": "HS256",
"typ": "JWT"
}Содержит утверждения: заявления о пользователе и дополнительные метаданные. Также представляет собой JSON, закодированный в Base64url. Данные payload НЕ шифруются, только подписываются — любой, кто перехватит токен, сможет прочитать его содержимое.
eyJzdWIiOiJ1c2VyXzEyMyIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTcxNzIwMDAwMH0{
"sub": "user_123",
"role": "admin",
"exp": 1717200000
}Криптографическая подпись над закодированными заголовком и payload. Верификация гарантирует, что токен не был изменён. Секретный ключ (HMAC) или приватный ключ (RSA/ECDSA) никогда не включается в состав токена.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5cHMACSHA256( base64url(header) + "." + base64url(payload), secret )
Стандартные утверждения JWT
Спецификация JWT определяет стандартные имена утверждений для обеспечения совместимости. Помимо зарегистрированных утверждений (registered claims), можно использовать публичные утверждения из реестра IANA или собственные приватные утверждения — главное избегать коллизий имён. Держите payload минимальным: он передаётся с каждым запросом.
| Утверждение | Название | Описание |
|---|---|---|
| iss | Issuer | Идентифицирует субъект, выдавший токен (например, URL вашего сервера аутентификации) |
| sub | Subject | Идентифицирует субъект токена (например, ID пользователя) |
| aud | Audience | Идентифицирует получателей токена (например, URL вашего API) |
| exp | Expiration | Unix-временная метка, после которой токен не принимается |
| nbf | Not Before | Unix-временная метка, до которой токен не принимается |
| iat | Issued At | Unix-временная метка выдачи токена — используется для определения возраста токена |
| jti | JWT ID | Уникальный идентификатор токена для предотвращения replay-атак |
Алгоритмы подписи
Алгоритм подписи объявляется в заголовке JWT. Правильный выбор критически важен для безопасности. Никогда не позволяйте клиенту самостоятельно выбирать алгоритм верификации — всегда явно указывайте допустимые алгоритмы на стороне сервера.
HS256 / HS384 / HS512Симметричный (HMAC)Использует общий секретный ключ для подписи и верификации. Прост в реализации, но требует, чтобы все верификаторы знали секрет — это усложняет управление ключами при большом числе сервисов.
RS256 / RS384 / RS512Асимметричный (RSA)Использует приватный ключ для подписи и публичный для верификации. Приватный ключ никогда не покидает сервер аутентификации. Публичный ключ можно безопасно распространять через JWKS endpoint, что упрощает верификацию в распределённых системах.
ES256 / ES384 / ES512Асимметричный (ECDSA)Как RSA, но с меньшими ключами и более быстрой подписью. Обеспечивает эквивалентную безопасность при лучшей производительности.
"alg": "none"Без подписи — ОПАСНОНет подписи. Токен с alg:none не имеет гарантий целостности и может быть подделан кем угодно. Ряд JWT-библиотек исторически принимал alg:none, что приводило к критическим уязвимостям.
Потоки аутентификации
- 1.Пользователь отправляет имя пользователя и пароль на endpoint входа
- 2.Сервер проверяет учётные данные по базе данных пользователей
- 3.Сервер подписывает JWT с утверждениями пользователя и сроком действия
- 4.Клиент сохраняет JWT (localStorage, cookie или память)
- 5.Клиент отправляет JWT в заголовке Authorization: Bearer для последующих запросов
- 1.Клиент перенаправляет пользователя на сервер авторизации с code challenge
- 2.Пользователь аутентифицируется и авторизует клиентское приложение
- 3.Сервер авторизации перенаправляет с одноразовым кодом авторизации
- 4.Клиент обменивает код + верификатор на access и refresh токены
- 5.Клиент использует access token для API-вызовов; refresh token для обновления
Распространённые уязвимости JWT
JWT безопасны при правильной реализации. Вот наиболее распространённые ошибки реализации, приводящие к уязвимостям:
Некоторые библиотеки принимают токены с alg:none. Злоумышленник может изменить payload и подделать любой JWT. Всегда явно указывайте и проверяйте допустимый алгоритм — никогда не доверяйте значению alg из заголовка токена без дополнительной проверки.
Если библиотека должна верифицировать RS256, но принимает HS256, злоумышленник может подписать токен, используя публичный ключ как HMAC-секрет. Всегда фиксируйте ожидаемый алгоритм на стороне сервера.
JWT без утверждения exp позволяет использовать токены неограниченно. Всегда устанавливайте короткий срок действия и явно проверяйте его при каждой верификации токена.
Payload JWT закодирован в Base64, а не зашифрован. Никогда не храните пароли, номера карт или другие секреты в утверждениях JWT — любой, кто получит токен, сможет прочитать его содержимое.
JWT, подписанные с короткими или предсказуемыми секретами, могут быть взломаны методом перебора в офлайн-режиме. Злоумышленник, получив токен, способен перебирать миллиарды ключей в секунду. Используйте криптографически случайные секреты длиной не менее 256 бит.
Принятие JWT без верификации подписи полностью доверяет payload. Это критическая ошибка, позволяющая любому пользователю создавать произвольные утверждения. Всегда верифицируйте подпись перед доверием любому утверждению.
Часто задаваемые вопросы
Стандартные JWT (JWS — JSON Web Signature) подписаны, но НЕ зашифрованы. Payload закодирован в Base64url, что тривиально обратимо — любой, кто получит токен, может прочитать его содержимое. JWE (JSON Web Encryption) обеспечивает шифрование, но используется значительно реже и добавляет сложность в управление ключами.
Access токены должны быть кратковременными: 5–60 минут — типичное значение. Долгоживущие токены опасны, поскольку их невозможно отозвать без ведения списка блокировки. Используйте refresh токены — более долгоживущие, хранящиеся безопасно — для получения новых access токенов по мере необходимости.
HttpOnly cookies — наиболее безопасный вариант, недоступный для JavaScript и защищающий от XSS. localStorage удобен, но уязвим к XSS-атакам. Хранение в памяти (JavaScript-переменная) наиболее безопасно, но токен не переживает перезагрузку страницы. Идеального варианта не существует — выбор зависит от модели угроз вашего приложения.
Нет без серверного состояния. Бесстатусная природа JWT означает, что сервер не может аннулировать их напрямую. Решения: короткий срок действия, список блокировки токенов в Redis (с хранением jti), ротация refresh токенов или переход на непрозрачные session токены для чувствительных операций.
Session cookies хранят случайный ID сессии; сервер ищет данные сессии в базе данных. JWT самодостаточны — сервер проверяет подпись без обращения к базе данных, что упрощает горизонтальное масштабирование. Однако JWT сложнее отозвать и они несут больше данных в каждом запросе.
Минимум: sub (ID пользователя), exp (срок действия) и iat (время выдачи). Добавьте iss (издатель) для многосервисных систем. Держите payload небольшим — JWT передаётся с каждым запросом. Не включайте чувствительные данные или большие объекты профиля.
RS256 (асимметричный) лучше для большинства продакшен-систем, поскольку только сервер аутентификации нуждается в приватном ключе. Несколько сервисов могут верифицировать токены с помощью публичного ключа без совместного использования секрета, что уменьшает поверхность атаки. HS256 проще, но требует безопасного распространения секрета среди всех верификаторов.
PKCE (Proof Key for Code Exchange) — расширение потока авторизационного кода OAuth2, предотвращающее атаки перехвата кода авторизации. Обязательно для публичных клиентов (SPA, мобильные приложения), которые не могут безопасно хранить client secret. Без PKCE злоумышленник, перехвативший код авторизации, может обменять его на токены от вашего имени.