Что такое JWT токен?

Эта статья посвящена детальному разбору JWT и его возможностей. Мы изучим структуру токена и построим его с нуля. Затем рассмотрим наиболее распространенные способы использования.

· 12 минуты на чтение
Что такое JWT токен?

Во время разработки сервисов с пользователями одной из типичных задач является реализация авторизации. Когда у вас один монолитный сервис, всё относительно просто:

  • Пользователь отправляет логин и пароль на сервер.
  • Сервер генерирует случайную строку (токен) по заданному алгоритму.
  • Сервер возвращает этот токен клиенту и запоминает, кому он был выдан.
  • При последующих запросах клиент отправляет этот токен в виде куки. Например, когда пользователь запрашивает информацию о себе, сервер проверяет токен и возвращает соответствующие персональные данные.

Этот токен обычно хранится в куках браузера, и поэтому его часто называют авторизационной кукой.

Однако ситуация усложняется при переходе на микросервисную архитектуру. Допустим, у нас есть отдельный сервис авторизации и несколько других микросервисов, отвечающих за различные части приложения: управление пользователями, блог, комментарии и т.д.

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

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

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

Спонсор поста

Формат JWT

JWT (JSON Web Token) состоит из трёх основных частей: заголовка (header), полезной нагрузки (payload) и подписи (signature). Заголовок и полезная нагрузка формируются в формате JSON, кодируются в Base64, после чего на их основе вычисляется подпись. Закодированные части соединяются в одну строку, и эта строка становится токеном, который используется для аутентификации и передачи данных.

В некоторых случаях подпись в JWT может отсутствовать. Этот вариант будет рассмотрен далее.

Заголовок является служебной частью и содержит информацию о типе токена (в данном случае JWT) и о применённом алгоритме подписи. Например:

{
  "typ": "JWT",
  "alg": "HS256"
}

Поле typ обозначает тип токена. Хотя некоторые приложения могут игнорировать это поле, стандарт рекомендует его включать для обеспечения совместимости.

Поле alg обязательно и указывает на алгоритм, используемый для подписи токена. В данном примере применяется алгоритм HMAC-SHA256 (HS256), который использует единый секретный ключ для подписи и проверки.

JWT также может использовать асимметричные алгоритмы подписи, например, RSA-SHA256 (RS256), где для подписи и проверки используются разные ключи. Стандарт поддерживает множество алгоритмов, таких как HS512, RS512, ES256, ES512, и даже алгоритм none, который указывает на отсутствие подписи. Если используется none, токен не подписан, и его подлинность не может быть проверена.

Закодируем этот JSON в base64 и получим: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Payload

Полезная нагрузка — это данные, которые передаются в JWT. Стандарт предусматривает несколько зарезервированных полей, которые называют “claims”:

  • iss — (issuer) издатель токена.
  • sub — (subject) назначение токена.
  • aud — (audience) аудитория, для которой предназначен токен.
  • exp — (expire time) срок действия токена.
  • nbf — (not before) время, до которого токен недействителен.
  • iat — (issued at) время создания токена.
  • jti — (JWT id) уникальный идентификатор токена.
Эти поля не являются обязательными, но важно использовать их правильно, чтобы избежать коллизий и ошибок в обработке.

Кроме зарезервированных полей, можно передавать любые данные по договорённости между сторонами. Например, следующий payload содержит информацию об издателе, назначении токена, времени истечения срока действия, времени выпуска и идентификаторе пользователя:

{
  "iss": "Auth Server",
  "sub": "auth",
  "exp": 1505467756869,
  "iat": 1505467152069,
  "user": 1
}
💽
Поскольку содержимое payload не шифруется, не рекомендуется передавать в нём чувствительные данные, такие как паспортные данные или пароли.
⚖️
Размер payload не ограничен, но его увеличение может негативно сказаться на производительности.

Закодируем этот payload в Base64 и получим вторую часть токена:

eyJpc3MiOiJBdXRoIFNlcnZlciIsInN1YiI6ImF1dGgiLCJleHAiOjE1MDU0Njc3NTY4NjksImlhdCI6MTUwNTQ2NzE1MjA2OSwidXNlciI6MX0.

Теперь у нас есть две из трёх частей токена — заголовок и полезная нагрузка. Осталось сгенерировать подпись.

Signature

Подпись генерируется следующим образом: закодированные в Base64 заголовок и полезная нагрузка объединяются через точку (.). Получившаяся строка хешируется с использованием алгоритма, указанного в заголовке (header). Результат этого хеширования и есть подпись.

Пример JWT-токена с тремя частями (заголовок, полезная нагрузка и подпись): eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJBdXRoIFNlcnZlciIsInN1YiI6ImF1dGgiLCJleHAiOjE1MDU0Njc3NTY4NjksImlhdCI6MTUwNTQ2NzE1MjA2OSwidXNlciI6MX0.9VPGwNXYfXnNFWH3VsKwhFJ0MazwmNvjSSRZ1vf3ZUU

Другие микросервисы могут проверять JWT-токен двумя способами:

  • Симметричный алгоритм (например, HS256): Все сервисы знают единый секретный ключ, который используется для подписи токенов.
  • Асимметричный алгоритм (например, RS256): В этом случае сервер авторизации использует приватный ключ для подписания токенов, а другие сервисы могут проверять подпись, используя соответствующий публичный ключ. Такой подход более безопасен, так как приватный ключ остаётся только у сервера авторизации.

Пример атаки: если злоумышленник попытается изменить данные в токене (например, роль пользователя на “admin”), он не сможет создать новую валидную подпись для этого токена, что делает подобные изменения бессмысленными.

Официальный сайт jwt.io предлагает два популярных алгоритма хэширования: HS256 (симметричный) и RS256 (асимметричный), но можно использовать и другие алгоритмы с приватным ключом, например ES256 или HS512.

Аутентификация

Схема аутентификации с использованием JWT проста и эффективна. Процесс выглядит следующим образом:

  • Пользователь вводит свои учётные данные в приложении или на доверенном сервисе аутентификации.
  • При успешной аутентификации сервис генерирует JWT-токен, который содержит сведения о пользователе.
  • Этот токен передаётся пользователю и сохраняется в браузере или приложении. При последующих запросах токен отправляется вместе с запросом на сервер: в cookie, заголовках HTTP-запроса, либо в параметрах POST или GET.
☣️
Передача токенов через GET-параметры нежелательна из-за риска утечек данных в логи или истории браузера. Предпочтительным способом передачи токена является использование заголовков или cookie с пометкой HttpOnly и Secure, чтобы минимизировать риски кражи токенов.

Получив JWT, приложение выполняет следующие шаги:

  • Проверка подписи: Приложение проверяет подпись токена, чтобы убедиться, что токен не был подделан.
  • Извлечение данных: После подтверждения подлинности токена приложение извлекает информацию о пользователе из полезной нагрузки (payload) токена.
  • Авторизация: На основе этих данных приложение предоставляет пользователю доступ к нужным ресурсам.
💡
Реализовать эту схему можно cамостоятельно, но существует множество библиотек для работы с JWT, доступных на сайте jwt.io, которые значительно упрощают процесс.

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

Использование JWT имеет ряд преимуществ по сравнению с классической схемой аутентификации на основе сессий:

Отсутствие необходимости хранения сессий на сервере.

В отличие от классической схемы, где сервер должен хранить информацию обо всех активных сессиях, JWT позволяет обойтись без этого. Когда пользователь отправляет запрос с токеном, приложению достаточно проверить его подпись и извлечь данные из полезной нагрузки. Это упрощает масштабирование приложения, так как серверу не нужно управлять сессиями.

Делегирование выдачи и валидации токенов.

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

Поддержка единого входа (SSO).

Используя отдельный сервис аутентификации, можно организовать единую точку входа (Single Sign-On, SSO) для нескольких приложений или сервисов. После прохождения аутентификации пользователь получает токен, который может быть использован для доступа ко всем доверенным сервисам без повторного ввода учётных данных.

Гибкость полезной нагрузки.

В полезную нагрузку JWT можно включать любые данные о пользователе, что позволяет приложению работать более эффективно. Например, в токен можно включить информацию о ролях пользователя или других атрибутах, чтобы не обращаться к базе данных для их получения при каждом запросе. Это может значительно повысить производительность при грамотной архитектуре.

Благодаря этим преимуществам JWT широко используется в корпоративных приложениях, особенно в микросервисной архитектуре. В таких системах каждый микросервис может извлекать необходимые сведения о пользователе напрямую из токена, не выполняя дополнительных запросов к базе данных или другим сервисам.

Спонсор поста 3

Уязвимости JWT

В этом разделе рассмотрим основные типы атак на JWT и методы их предотвращения.

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

Если JWT хранится в открытом виде (например, в localStorage), любой вредоносный скрипт может получить к нему доступ. В результате, даже если токен имеет короткий срок действия, он может быть использован злоумышленником до его истечения для выполнения вредоносных действий.

Перехват JWT может привести к серьёзным последствиям. Рассмотрим основные риски:

Извлечение данных из токена

JWT передаётся в открытом виде, а его полезная нагрузка (payload) кодируется с помощью Base64Url. Это не является шифрованием, и любой злоумышленник, перехвативший токен, может декодировать payload и получить доступ к содержащимся в нём данным, применив функцию base64UrlDecode. В случае утечки токена злоумышленник сможет извлечь информацию о пользователе, такую как идентификатор или другие персональные данные.

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

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

Рекомендации по предотвращению

  • Используйте HTTPS для всех запросов, передающих JWT, чтобы предотвратить перехват токена.
  • Использование HttpOnly cookie. Вместо хранения JWT в localStorage или sessionStorage, лучше использовать HttpOnly cookies. Эти cookie недоступны для JavaScript, что значительно снижает риск кражи токена через XSS-атаки 
  • Не храните в JWT чувствительные данные. В полезной нагрузке стоит передавать только минимально необходимые данные, такие как обезличенные идентификаторы пользователей или данные, которые не представляют угрозу в случае утечки.
  • Ограничьте срок действия JWT. Это минимизирует время, в течение которого перехваченный токен остаётся активным. Обычно рекомендуется устанавливать срок действия токена на несколько минут.
  • Используйте механизм обновления токенов (refresh tokens). В случае короткого срока действия JWT можно использовать refresh-токен для безопасного получения нового JWT без необходимости повторной аутентификации. Refresh-токены также должны быть передаваемы только по защищённому соединению и иметь более строгие механизмы контроля.

Refresh tokens

В современных схемах аутентификации с использованием JWT, после успешной аутентификации пользователь получает два токена:

  • Access token — JWT, который используется для идентификации и авторизации пользователя при запросах к приложению.
  • Refresh token — токен произвольного формата, предназначенный для безопасного обновления access token, когда срок его действия истекает.

Access token имеет ограниченное время жизни (например, одну минуту), что минимизирует риски при его компрометации. Refresh token, напротив, действует дольше (например, день, неделю или месяц), но он одноразовый — его можно использовать только для получения нового access token.

Схема аутентификации в таком случае выглядит следующим образом:

  • Пользователь проходит аутентификацию и получает от сервера два токена: access token и refresh token.
  • При каждом запросе к защищённому ресурсу пользователь отправляет access token, и сервер на его основе идентифицирует и авторизует пользователя.
  • Когда срок действия access token истекает, клиент отправляет refresh token, чтобы получить новый access token и, как правило, новый refresh token.
  • Когда срок действия refresh token истекает, пользователь должен пройти аутентификацию снова.

Рекомендации по безопасности при работе с refresh tokens:

  • Храните refresh token в защищённом месте. Например, в HttpOnly и Secure cookie, чтобы предотвратить его кражу через JavaScript (XSS-атаки).
  • Реализуйте механизмы отзыва refresh tokens. В случае компрометации, сервер должен иметь возможность отозвать refresh token и не допустить его использования.
  • Ограничьте срок жизни refresh token и обеспечьте возможность его однократного использования, чтобы минимизировать риск его повторного применения злоумышленником.

Подбор ключа симметричного алгоритма подписи

При использовании симметричных алгоритмов для подписи JWT (например, HS256, HS512), существует риск подбора ключевой фразы злоумышленником. Если злоумышленнику удастся подобрать ключ, он сможет манипулировать JWT, как если бы он был частью системы. Это позволит ему генерировать поддельные токены, выдавая себя за любого пользователя, и получать доступ к защищённым ресурсам.

Например, если для подписи JWT используется слабая ключевая фраза, как в случае строки "password", злоумышленник легко подберёт её, используя программы для взлома, такие как John the Ripper или hashcat. Простые ключи, такие как "password", содержатся в большинстве словарей для перебора паролей и являются первыми кандидатами для атаки.

Рекомендации по защите от подбора ключа:

  • Используйте надёжные ключевые фразы: Ключевая фраза для подписи JWT должна быть длинной и сложной. Рекомендуется использовать комбинации заглавных и строчных букв латинского алфавита, цифр и специальных символов. Например, строка длиной 32 символа с хорошим сочетанием символов существенно усложнит задачу для злоумышленников и сделает подбор ключа практически невозможным.
  • Храните ключи в строгой конфиденциальности: Никогда не храните ключи в открытом виде в коде или файлах конфигурации, доступных посторонним. Используйте безопасные хранилища секретов и убедитесь, что доступ к ним строго ограничен.
  • Регулярно меняйте ключи: Периодическая смена ключевой фразы значительно усложнит жизнь злоумышленнику, даже если ему удастся приблизиться к её подбору. Однако следует учитывать, что смена ключа приведёт к аннулированию всех текущих токенов, и пользователям придётся повторно проходить аутентификацию. Этот компромисс между безопасностью и удобством стоит учитывать при настройке частоты смены ключа.

Использование алгоритма none

Как упоминалось ранее, если в заголовке JWT указан алгоритм none, это означает, что токен не был подписан. В таком случае токен не имеет подписи, что делает невозможным проверку его подлинности. Это может быть использовано злоумышленниками для атак, позволяющих подделать данные в токене.

Рассмотрим, как такая атака может быть проведена на конкретном примере. Допустим, наш JWT в незакодированном виде выглядит так:

header:
{
    "typ": "JWT",
    "alg": "HS256"
}
payload:
{
    "id": "1337",
    "username": "bizone",
    "iat": 1594209600,
    "role": "user"
}
signature:
ZvkYYnyM929FM4NW9_hSis7_x3_9rymsDAx9yuOcc1I

Предположим, что злоумышленник хочет повысить свои привилегии и сделать себя администратором, изменив значение поля role с "user" на "admin". Однако при этом подпись JWT станет невалидной, и приложение отклонит токен.

Для обхода этого механизма злоумышленник может попытаться изменить значение поля alg в заголовке на "none", чтобы отключить проверку подписи. После этого JWT будет выглядеть следующим образом:

header:
{
    "typ": "JWT",
    "alg": "none"
}
payload:
{
    "id": "1337",
    "username": "bizone",
    "iat": 1594209600,
    "role": "admin"
}

Так как теперь подпись отсутствует, закодированный JWT примет вид: eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJpZCI6IjEzMzciLCJ1c2VybmFtZSI6ImJpem9uZSIsImlhdCI6MTU5NDIwOTYwMCwicm9sZSI6ImFkbWluIn0

Этот поддельный токен злоумышленник отправляет на сервер. Уязвимое приложение, проверив заголовок и увидев alg: none, может ошибочно принять токен как валидный и предоставить злоумышленнику доступ с правами администратора.

Как защититься от атаки с использованием none:

  • Используйте белый список алгоритмов: На стороне сервера необходимо настроить белый список разрешённых алгоритмов подписи и отклонять все JWT с алгоритмом none. Это гарантирует, что только токены, подписанные разрешёнными алгоритмами, будут приняты.
  • Используйте один алгоритм: Для минимизации рисков лучше всего использовать один алгоритм для подписи JWT. Например, HS256 (симметричный алгоритм) или RS256 (асимметричный алгоритм). Это упрощает управление безопасностью и снижает вероятность ошибок.

Изменение алгоритма подписи

При использовании асимметричных алгоритмов, таких как RS256, подпись токена осуществляется с использованием приватного ключа, а проверка подписи — с использованием публичного ключа.

В некоторых случаях злоумышленники могут воспользоваться уязвимостями, связанными с проверкой JWT. Например, если токен подписан асимметричным алгоритмом (RS256), а сервер допускает использование симметричного алгоритма (HS256), злоумышленник может подделать токен, используя публичный ключ как секретный ключ.

Предположим, у нас есть JWT, подписанный с использованием асимметричного алгоритма RS256:

header:
{
    "alg": "RS256",
    "typ": "JWT"
}
payload:
{
    "id": "1337",
    "username": "bizone",
    "iat": 1594209600,
    "role": "user"
}
signature:
YLOVSKef-paSnnM8P2JLaU2FiS8TbhYqjewLmgRJfCj1Q6rVehAHQ-lABnKoRjlEmHZX-rufHEocDxGUYiGMjMexUQ3zt-WqZITvozJ4pkvbV-mJ1nKj64NmqaR9ZkBWtmF-PHJX50eYjgo9rzLKbVOKYOUa5rDkJPHP3U0aaBXFP39zsGdOTuELv436WXypIZBeRq2yA_mDH13TvzegWCK5sjD4Gh177bCq57tBYjhGIQrDypVe4cWBPlvwFlmG8tdpWGu0uFp0GcbTAfLUlbTSuGROj88BY0XeUs0iqmGlEICES3uqNx7vEmdT5k_AmL436SLedE0VHcyxve5ypQ

В кодированном виде он будет выглядеть следующим образом: eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjEzMzciLCJ1c2VybmFtZSI6ImJpem9uZSIsImlhdCI6MTU5NDIwOTYwMCwicm9sZSI6InVzZXIifQ.YLOVSKef-paSnnM8P2JLaU2FiS8TbhYqjewLmgRJfCj1Q6rVehAHQ-lABnKoRjlEmHZX-rufHEocDxGUYiGMjMexUQ3zt-WqZITvozJ4pkvbV-mJ1nKj64NmqaR9ZkBWtmF-PHJX50eYjgo9rzLKbVOKYOUa5rDkJPHP3U0aaBXFP39zsGdOTuELv436WXypIZBeRq2yA_mDH13TvzegWCK5sjD4Gh177bCq57tBYjhGIQrDypVe4cWBPlvwFlmG8tdpWGu0uFp0GcbTAfLUlbTSuGROj88BY0XeUs0iqmGlEICES3uqNx7vEmdT5k_AmL436SLedE0VHcyxve5ypQ

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

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnzyis1ZjfNB0bBgKFMSv
vkTtwlvBsaJq7S5wA+kzeVOVpVWwkWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHc
aT92whREFpLv9cj5lTeJSibyr/Mrm/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIy
tvHWTxZYEcXLgAXFuUuaS3uF9gEiNQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0
e+lf4s4OxQawWD79J9/5d3Ry0vbV3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWb
V6L11BWkpzGXSW4Hv43qa+GSYOD2QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9
MwIDAQAB
-----END PUBLIC KEY-----

Публичный ключ

-----BEGIN RSA PRIVATE KEY-----
MIIEogIBAAKCAQEAnzyis1ZjfNB0bBgKFMSvvkTtwlvBsaJq7S5wA+kzeVOVpVWw
kWdVha4s38XM/pa/yr47av7+z3VTmvDRyAHcaT92whREFpLv9cj5lTeJSibyr/Mr
m/YtjCZVWgaOYIhwrXwKLqPr/11inWsAkfIytvHWTxZYEcXLgAXFuUuaS3uF9gEi
NQwzGTU1v0FqkqTBr4B8nW3HCN47XUu0t8Y0e+lf4s4OxQawWD79J9/5d3Ry0vbV
3Am1FtGJiJvOwRsIfVChDpYStTcHTCMqtvWbV6L11BWkpzGXSW4Hv43qa+GSYOD2
QU68Mb59oSk2OB+BtOLpJofmbGEGgvmwyCI9MwIDAQABAoIBACiARq2wkltjtcjs
kFvZ7w1JAORHbEufEO1Eu27zOIlqbgyAcAl7q+/1bip4Z/x1IVES84/yTaM8p0go
amMhvgry/mS8vNi1BN2SAZEnb/7xSxbflb70bX9RHLJqKnp5GZe2jexw+wyXlwaM
+bclUCrh9e1ltH7IvUrRrQnFJfh+is1fRon9Co9Li0GwoN0x0byrrngU8Ak3Y6D9
D8GjQA4Elm94ST3izJv8iCOLSDBmzsPsXfcCUZfmTfZ5DbUDMbMxRnSo3nQeoKGC
0Lj9FkWcfmLcpGlSXTO+Ww1L7EGq+PT3NtRae1FZPwjddQ1/4V905kyQFLamAA5Y
lSpE2wkCgYEAy1OPLQcZt4NQnQzPz2SBJqQN2P5u3vXl+zNVKP8w4eBv0vWuJJF+
hkGNnSxXQrTkvDOIUddSKOzHHgSg4nY6K02ecyT0PPm/UZvtRpWrnBjcEVtHEJNp
bU9pLD5iZ0J9sbzPU/LxPmuAP2Bs8JmTn6aFRspFrP7W0s1Nmk2jsm0CgYEAyH0X
+jpoqxj4efZfkUrg5GbSEhf+dZglf0tTOA5bVg8IYwtmNk/pniLG/zI7c+GlTc9B
BwfMr59EzBq/eFMI7+LgXaVUsM/sS4Ry+yeK6SJx/otIMWtDfqxsLD8CPMCRvecC
2Pip4uSgrl0MOebl9XKp57GoaUWRWRHqwV4Y6h8CgYAZhI4mh4qZtnhKjY4TKDjx
QYufXSdLAi9v3FxmvchDwOgn4L+PRVdMwDNms2bsL0m5uPn104EzM6w1vzz1zwKz
5pTpPI0OjgWN13Tq8+PKvm/4Ga2MjgOgPWQkslulO/oMcXbPwWC3hcRdr9tcQtn9
Imf9n2spL/6EDFId+Hp/7QKBgAqlWdiXsWckdE1Fn91/NGHsc8syKvjjk1onDcw0
NvVi5vcba9oGdElJX3e9mxqUKMrw7msJJv1MX8LWyMQC5L6YNYHDfbPF1q5L4i8j
8mRex97UVokJQRRA452V2vCO6S5ETgpnad36de3MUxHgCOX3qL382Qx9/THVmbma
3YfRAoGAUxL/Eu5yvMK8SAt/dJK6FedngcM3JEFNplmtLYVLWhkIlNRGDwkg3I5K
y18Ae9n7dHVueyslrb6weq7dTkYDi3iOYRW8HRkIQh06wEdbxt0shTzAJvvCQfrB
jg/3747WSsf/zBTcHihTRBdAv6OmdhV4/dD5YBfLAkLrd+mX7iE=
-----END RSA PRIVATE KEY-----

Приватный ключ

Для тестов мы будем использовать сайт jwt.io

Исходный JWT

Как и в предыдущем примере, модифицируем токен:

header:
{
    "typ": "JWT",
    "alg": "HS256"
}
payload:
{
    "id": "1337",
    "username": "bizone",
    "iat": 1594209600,
    "role": "admin"
}

В кодированном виде заголовок и зполезная нагрузка будут выглядеть следующим образом:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEzMzciLCJ1c2VybmFtZSI6ImJpem9uZSIsImlhdCI6MTU5NDIwOTYwMCwicm9sZSI6ImFkbWluIn0

Остается только подсчитать подпись с использованием публичного ключа сервиса.

Для начала переводим ключ в hex-представление:

Переводим ключ в hex

Затем генерируем подпись с использованием openSSL:

Генерируем подпись

Полученное значение E1R1nWNsO-H7h5WoYCBnm6c1zZy-0hu2VwpWGMVPK2g добавляем к уже имеющейся строке, и наш токен принимает следующий вид: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEzMzciLCJ1c2VybmFtZSI6ImJpem9uZSIsImlhdCI6MTU5NDIwOTYwMCwicm9sZSI6ImFkbWluIn0.E1R1nWNsO-H7h5WoYCBnm6c1zZy-0hu2VwpWGMVPK2g

Подставляем в поле secret на jwt.io наш публичный ключ, и JWT успешно проходит проверку. Не забудьте поставить галочку secret base64 encoded.

Проверка JWT

Для предотвращения такой атаки рекомендуется:

  • Использовать белый список алгоритмов. Разрешать только конкретные алгоритмы подписи на сервере, например, RS256. Все токены с неподдерживаемыми алгоритмами должны быть отклонены.
  • Строго разделять симметричные и асимметричные алгоритмы. Если используется асимметричная схема, как RS256, то симметричные алгоритмы (например, HS256) не должны быть допустимы.
  • Проверка библиотеки. Убедитесь, что библиотека для работы с JWT правильно реализует проверку подписей, и не использует публичный ключ как секретную фразу при симметричных алгоритмах.

Заключение

SON Web Tokens (JWT) — это мощная и гибкая технология, которая при правильном использовании значительно упрощает процессы аутентификации и авторизации. JWT позволяет избежать ошибок, связанных с классическими сессионными подходами, даёт возможность легко разделить информационные потоки между микросервисами и организовать единую точку входа для разных сервисов (SSO). Это может также повысить производительность системы, так как токены являются стейтлесс (не требуют хранения на сервере) и передаются клиенту для последующего использования.

Однако неправильное использование JWT может привести к серьёзным уязвимостям и компрометации безопасности всей системы, что может затронуть всех её пользователей. Важные рекомендации для безопасного использования JWT:

  • Использование HTTPS: Все JWT должны передаваться исключительно через защищённое соединение, чтобы избежать их перехвата злоумышленниками.
  • Минимизация данных в токене: Не следует передавать в токене чувствительные данные, такие как пароли или личные данные. В JWT стоит включать лишь минимальный набор информации, необходимый для работы системы
  • Ограничение срока жизни JWT: Следует настраивать короткий срок действия токенов и использовать механизмы обновления с помощью refresh tokens, чтобы сократить временное окно для злоупотреблений скомпрометированным токеном
  • Надежные ключи и алгоритмы: Использование длинных, надёжных ключевых фраз и проверенных алгоритмов (например, RS256) помогает предотвратить атаки, связанные с подбором ключей или использованием слабых алгоритмов подписи
  • Регулярная смена ключей: Это снижает риск злоумышленников, даже если им удастся получить текущий ключ.
  • Белый список алгоритмов: На стороне сервера следует использовать только те алгоритмы, которые вы явно разрешили, чтобы избежать атак, связанных с изменением алгоритма подписи (например, подмена RS256 на HS256). В идеале работать строго с одним алгоритмом подписи;
  • Использование проверенных библиотек: Важно использовать известные библиотеки, которые прошли аудит безопасности и поддерживают лучшие практики.
  • Валидация данных от пользователя: Все данные, которые поступают от пользователя, должны тщательно проверяться и очищаться (санитизироваться), чтобы предотвратить XSS-атаки и внедрение вредоносного кода.

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

После краткого знакомства с JWT (JSON Web Tokens) может возникнуть впечатление, что они являются неотъемлемой частью механизмов авторизации и аутентификации, таких как OAuth 2.0 или OpenID Connect. Однако это не совсем так. JWT — это лишь формат передачи данных, который используется в этих системах, но не является их внутренней частью.

Подробнее о реализации аутентификации с использованием JWT можно узнать в отдельной статье 👇

Реализация JWT в Spring Boot
В этой статье мы реализуем JWT авторизацию с использованием Spting-Boot приложений.
Struchkov Mark
Struchkov Mark
Задавайте вопросы, если что-то осталось не понятным👇