Skip to content

Latest commit

 

History

History
220 lines (142 loc) · 18.3 KB

File metadata and controls

220 lines (142 loc) · 18.3 KB
layout title tags
col-document
WSTG - Latest
WSTG

{% include breadcrumb.html %}

Тестирование JSON Web Token

ID
WSTG-SESS-10

Обзор

JSON Web Token (JWT) — это токен JSON с криптографической подписью, предназначенный для обмена утверждениями (англ.: claims) между системами. Они часто используются в качестве токенов аутентификации или сессии, особенно в REST API.

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

Задачи тестирования

  • Определить, раскрывают ли JWT чувствительную информацию.
  • Определить, можно ли подделать или модифицировать JWT.

Как тестировать

Состав

JWT состоит из трёх компонентов:

  • Заголовок
  • Полезная нагрузка (или тело)
  • Подпись

Каждый компонент закодирован в Base64, и они разделены точками (.). Обратите внимание, что кодировка Base64, используемая в JWT, удаляет знаки равенства (=), поэтому вам может потребоваться вернуть их для декодирования.

Анализ компонентов

Заголовок

Заголовок определяет тип токена (обычно JWT) и алгоритм, используемый для подписи. Пример декодированного заголовка показан ниже:

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

Есть три основных типа алгоритмов, которые используются для подписи:

Алгоритм Описание
HSxxx HMAC с использованием секретного ключа и SHA-xxx.
RSxxx и PSxxx Подпись с открытым ключом с использованием RSA.
ESxxx Подпись с открытым ключом с использованием ECDSA.

Есть также множество других алгоритмов, которые можно использовать для зашифрованных токенов (JWE), хотя они менее распространены.

Полезная нагрузка

Полезная нагрузка JWT содержит сами данные. Пример полезной нагрузки показан ниже:

{
  "username": "admininistrator",
  "is_admin": true,
  "iat": 1516239022,
  "exp": 1516242622
}

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

Данный JWT включает имя пользователя и его статус администратора, а также два стандартных утверждения (iat и exp). Эти утверждения определены в RFC 5719, их краткое изложение приведено в таблице ниже:

Утверждение Наименование Описание
iss Issuer Идентификатор стороны, выпустившей токен.
iat Issued At Unix-время, указывающее на то, когда был выпущен токен.
nbf Not Before Unix-время самой ранней даты, начиная с которой токен может быть использован.
exp Expires Unix-время, когда срок действия токена истечёт.

Подпись

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

Анализ использования

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

Достоверность JWT также необходимо проверить по утверждениям iat, nbf и exp, чтобы убедиться в том, что:

  • JWT имеет разумный срок действия.
  • Токены с истекшим сроком действия отклоняются.

Проверка подписи

Одна из наиболее серьёзных уязвимостей, встречающихся в JWTs, заключается в том, что приложению не удаётся проверить правильность подписи. Обычно это происходит, когда разработчик использует, например, такую функцию, как jwt.decode() в NodeJS, которая просто декодирует тело JWT, не верифицируя перед этим подпись с помощью функции jwt.verify().

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

Алгоритм None

Помимо алгоритмов с открытым ключом и на основе HMAC, спецификация JWT также определяет алгоритм подписи, называемый none. Как следует из названия, это означает, что в JWT нет подписи, что позволяет изменять его.

Это можно проверить, изменив алгоритм подписи (alg) в заголовке JWT на none, как показано в примере ниже:

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

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

eyJhbGciOiAibm9uZSIsICJ0eXAiOiAiSldUIn0K.eyJ1c2VybmFtZSI6ImFkbWluaW5pc3RyYXRvciIsImlzX2FkbWluIjp0cnVlLCJpYXQiOjE1MTYyMzkwMjIsImV4cCI6MTUxNjI0MjYyMn0.

Некоторые реализации пытаются избежать этого, явно блокируя использование алгоритма none. Если это делается без учёта регистра, можно попробовать указать алгоритм как NoNe.

«Психоподписи» ECDSA

В версиях Java с 15 по 18 была выявлена уязвимость, из-за которой при некоторых обстоятельствах они неправильно проверяли подписи ECDSA (CVE-2022-21449, известная как «психоподпись», отсылка к «психобумаге»). Если одна из этих уязвимых версий используется для анализа JWT с алгоритмом ES256, с её помощью можно полностью обойти проверку подписи путём подделки тела и последующей замены подписи на значение

MAYCAQACAQA

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

eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJhZG1pbiI6InRydWUifQ.MAYCAQACAQA

Некриптостойкие ключи HMAC

Если JWT подписан с использованием алгоритма, основанного на HMAC (например, HS256), стойкость подписи полностью зависит от энтропии секретного ключа HMAC.

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

Если значения по умолчанию нет, то ключ можно взломать, угадать или перебрать. Самый простой способ сделать это — использовать скрипт crackjwt.py, которому нужен только сам JWT и файл словаря.

Более серьёзный вариант — преобразовать JWT в формат, понятный John the Ripper, скриптом jwt2john.py. Затем можно использовать Джона для проведения гораздо более сложных атак на ключ.

Если JWT большой, он может превышать максимально допустимый размер, поддерживаемый Джоном. Это можно обойти, увеличив значение переменной SALT_LIMBS в /src/hmacSHA256_fmt_plug.c (или аналогичном ему для других форматов HMAC) и перекомпилировав Джона, как описано в дискуссии на GitHub.

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

Путаница между HMAC и открытым ключом

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

  1. Приложение должно ожидать, что JWT будет подписан алгоритмом на основе открытого ключа (т.е. RSxxx или ESxxx).
  2. Приложение не должно проверять, какой алгоритм JWT фактически использует для подписи.
  3. Открытый ключ, используемый для проверки JWT, должен быть доступен злоумышленнику.

Если все эти условия выполняются, злоумышленник может применить открытый ключ для подписи JWT к алгоритму на основе HMAC (HS256). Например, в библиотеке Node.JS jsonwebtoken и для открытого ключа, и для токенов на основе HMAC используется одна и та же функция, как показано в примере ниже:

// Проверка JWT, подписанного с помощью RS256
jwt.verify(token, publicKey);

// Проверка JWT, подписанного с помощью HS256
jwt.verify(token, secretKey);

Это означает, что если JWT подписан publicKey в качестве секретного ключа для алгоритма HS256, подпись будет считаться действительной.

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

openssl s_client -connect example.org:443 | openssl x509 -pubkey -noout

Также открытый ключ может храниться в общедоступном файле на сайте в типичном каталоге, например /.well-known/jwks.json.

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

Открытый ключ, предоставленный злоумышленником

Стандарт JSON Web Signature (JWS) (который определяет заголовок и подписи в JWT), позволяет встраивать ключ для подписи токена, в заголовок. Если библиотека проверки токена поддерживает это и не проверяет ключ по списку утверждённых, то злоумышленник может подписать JWT произвольным ключом.

Существует множество скриптов, которые можно использовать для этого, например, jwk-node-jose.py или jwt_tool.

Связанные сценарии тестирования

Меры защиты

  • Используйте безопасную и актуальную библиотеку для обработки JWT.
  • Убедитесь, что подпись действительна и что она использует ожидаемый алгоритм.
  • Используйте криптостойкий ключ HMAC или уникальный закрытый ключ для подписи.
  • Убедитесь, что в полезной нагрузке нет чувствительной информации.
  • Убедитесь, что JWT защищены при хранении и передаче.
  • См. Памятка OWASP по JSON Web Token.

Инструменты

Ссылки