JSON Web Token (JWT) — это открытый стандарт (RFC 7519) для создания токенов доступа на основе формата JSON. Обычно используется для передачи данных аутентификации в клиент-серверных приложениях. Токены создаются сервером, подписываются секретным ключом и передаются клиенту, который позже использует этот токен для подтверждения своей личности.
Заголовок
Это объект JSON, который представляет собой метаданные токена. Чаще всего состоит из двух полей:
- Тип токена
- Алгоритм хэширования
Официальный сайт предлагает два алгоритма хэширования:
- «HS256»
- «RS256»
Но на самом деле любой алгоритм с приватным ключом может быть использован.
Полезная нагрузка
Это также объект JSON, который используется для хранения такой информации о пользователе, как:
- идентификатор
- имя пользователя
- роль
- время генерации токена и т.д.
Подпись
Это наиболее важная часть, поскольку она определяет целостность токена путем подписания заголовка и полезной нагрузки в кодировке Base64-URL, разделенных точкой (.) с секретным ключом. Например, чтобы сгенерировать токен с помощью алгоритма HS256, псевдокод будет таким:
// Use Base64-URL algorithm for encoding and concatenate with a dotdata = (base64urlEncode(header) + '.' + base64urlEncode(payload))// Use HS256 algorithm with "SECRET_KEY" string as a secretsignature = HMACSHA256(data , SECRET_KEY)// Complete token
JWT = data + "." + base64UrlEncode(signature)
Что такое SECRET_KEY?
Как правило, JWT может быть сгенерирован с помощью двух механизмов шифрования, таких как:
- Симметричное
- Ассиметричное
Симметричное шифрование:
Этот механизм требует единственного ключа для создания и проверки JWT.
Например, пользователь «Vasya» сгенерировал JWT с «h1dd1n_m1ss1g3» в качестве секретного ключа. Любой человек, знающий этот ключ, может с его помощью изменить токен. JWT при этом останется действительным.
Самый распространенный алгоритм для этого типа — HS256.
Асимметричное шифрование:
Этот механизм требует открытого ключа для проверки и закрытого ключа для подписи.
Например, если «Vasya» использовал это шифрование, то он единственный, кто может создать новый токен, используя закрытый ключ, тогда как «Petya» может только проверить токен с помощью открытого ключа, но не может его изменить.
Наиболее распространенный алгоритм для этого типа — RS256.
Атаки на JWT
Чтобы подделать токен, вам необходимо иметь правильные ключи (например, закрытый ключ для HS256, открытый и закрытый ключи для RS256), но если конфигурация JWT не реализована правильно, то есть много способов обойти элементы управления, которые позволяют изменить токен и получить несанкционированный доступ.
Базовые атаки
Для выполнения всех этих атак нам понадобиться JWT_Tool
1. Нет алгоритма
Если приложению не удается проверить значение заголовка «alg», то мы можем изменить его значение на «none», и таким образом оно исключает необходимость действительной подписи для проверки. Например:
// Modified Header of JWT after changing the "alg" parameter{
"alg": "none",
"typ": "JWT"
}
Команда:
python3 jwt_tool.py <JWT> -X a
Здесь jwt_tool создал различные полезные нагрузки для использования этой уязвимости и обхода всех ограничений, пропустив раздел «Подпись».
2. Изменяем алгоритм с RS256 на HS256
Как указано выше, алгоритм RS256 требует закрытого ключа для управления данными и соответствующего открытого ключа для аутентификации подписи. Однако, если мы сможем изменить алгоритм подписи с RS256 на HS256, это заставим приложение использовать только один ключ для обеих задач. Это нормальное поведение алгоритма HMAC.
Таким образом рабочий процесс будет преобразован из асимметричного в симметричное шифрование. Теперь мы можем подписывать новые токены тем же открытым ключом.
Команда:
python3 jwt_tool.py <JWT> -S hs256 -k public.pem
В данном случае мы сначала загружаем открытый ключ (public.pem) из приложения, а затем подписываем токен с помощью алгоритма HS256, используя этот ключ. Таким образом, мы можем создавать новые токены и вставлять полезную нагрузку в любое существующее утверждение.
3. Без проверки подписи
Иногда при широковещательной передаче данных в заголовке и в разделе полезной нагрузки приложение не возвращает ошибку. Это означает, что подпись не проверяется после подписания сервером авторизации. Таким образом, мы можем вставить любую полезную нагрузку в приложение, и токен всегда будет действительным.
Команда:
python3 jwt_tool.py <JWT> -I -pc name -pv admin
Здесь часть подписи не проверяется, а значит можно смягчить утверждение «имени» в разделе полезной нагрузки, сделав себя «администратором».
4. Взлом секретного ключа
Мы можем получить доступ к файлу SECRET_KEY с помощью уязвимостей, таких как
- LFI
- XXE
- SSRF
Если это невозможно, то все равно можно провести другие атаки, чтобы проверить, использует ли токен какую-либо слабую секретную строку для шифрования.
Для этой цели можно использовать расширение BurpSuite под названием JWT Heartbreaker.
Такое раскрытие поставит под угрозу весь механизм безопасности, поскольку теперь мы можем генерировать произвольные токены с секретным ключом.
Но чтобы убедиться, что полученная нами строка является действительным ключом SECRET_KEY или нет? Мы можем использовать функцию Crack в jwt_tool.
Команда:
python3 jwt_tool.py <JWT> -C -d secrets.txt
// Use -p flag for a string
5. Использование произвольных файлов для проверки
Key ID (kid) — это необязательный строковый заголовок, который используется для идентификации конкретного ключа, присутствующего в файловой системе или базе данных, а затем использует его содержимое для проверки подписи. Этот параметр полезен, если приложение имеет несколько ключей для подписи токенов, но может быть опасным, если он является инъекционным, поскольку в этом случае злоумышленник может указать на конкретный файл, содержимое которого предсказуемо.
Например, «/dev/null» называется нулевым файлом устройства и всегда ничего не возвращает, поэтому он отлично работает в системах на основе Unix.
Команда:
python3 jwt_tool.py <JWT> -I -hc kid -hv "../../dev/null" -S hs256 -p ""
В качестве альтернативы можно использовать любой файл, присутствующий в корневом веб-каталоге, например, CSS или JS. Также можно использовать его содержимое для проверки подписи.
Другое решение проблемы:
python3 jwt_tool.py -I -hc kid -hv "путь / к / файлу" -S hs256 -p "Содержимое файла"
Продвинутые атаки:
1. SQL-инъекция
Эта уязвимость может возникнуть, если любой параметр, который извлекает какое-либо значение из базы данных, не очищается должным образом. Благодаря чему можно решать CTF задачи.
Например, если приложение использует алгоритм RS256, но открытый ключ виден в заявлении «pk» в разделе Payload, тогда можно преобразовать алгоритм подписи в HS256 и создавать новые токены.
Команда для подсчета количества столбцов:
python3 jwt_tool.py <JWT> -I -pc name -pv "imparable' ORDER BY 1--" -S hs256 -k public.pem// Increment the value by 1 until an error will occur
2. Параметр поддельного заголовка
1. SQL-инъекция
Эта уязвимость может возникнуть, если любой параметр, который извлекает какое-либо значение из базы данных, не очищается должным образом. Благодаря чему можно решать CTF задачи.
Например, если приложение использует алгоритм RS256, но открытый ключ виден в заявлении «pk» в разделе Payload, тогда можно преобразовать алгоритм подписи в HS256 и создавать новые токены.
Команда для подсчета количества столбцов:
python3 jwt_tool.py <JWT> -I -pc name -pv "imparable' ORDER BY 1--" -S hs256 -k public.pem// Increment the value by 1 until an error will occur
2. Параметр поддельного заголовка
JSON Web Key Set (JWKS) — это набор открытых ключей, которые используются для проверки токена. Вот пример:
Этот файл хранится на доверенном сервере, приложение может указывать на этот файл через параметры заголовка:
- «jku»
- «x5u»
Но мы можем управлять URL-адресом с помощью таких уловок, как:
- открытый редирект
- добавление символа @ после имени хоста и т. д.
Затем мы можем перенаправить Приложение на наш вредоносный сервер вместо доверенного сервера и генерировать новые токены, так как мы контролируем как открытые, так и закрытые ключи.
JSON Set URL (jku):
Этот параметр указывает на набор открытых ключей в формате JSON (атрибуты n и e в JWKS), а «jwt_tool» автоматически создает файл JWKS с именем «jwttool_custom_jwks.json» для этой атаки при первом запуске инструмента после установки.
Команда:
python3 jwt_tool.py <JWT> -X s -ju "https://attacker.com/jwttool_custom_jwks.json"
X.509 URL (x5u):
Этот параметр указывает на сертификат открытого ключа X.509 или цепочку сертификатов (атрибут x5c в JWKS). Вы можете сгенерировать этот сертификат с соответствующим закрытым ключом следующим образом:
openssl req -newkey rsa:2048 -nodes -keyout private.pem -x509 -days 365 -out attacker.crt -subj "/C=AU/L=Brisbane/O=CompanyName/CN=pentester"
Здесь с использованием OpenSSL сертификат был создан в «attacker.crt», который теперь может быть встроен в файл JWKS с атрибутом «x5c», а его эксплуатация может осуществляться следующим образом:
python3 jwt_tool.py <JWT> -S rs256 -pr private.pem -I -hc x5u -hv "https://attacker.com/custom_x5u.json"
Встроенные открытые ключи:
Если сервер воплощает открытые ключи непосредственно в токене с помощью параметров «jwk» (JSON Web Key) или «x5c» (цепочка сертификатов X.509), попробуйте заменить их своими собственными открытыми ключами и подписать токен соответствующим закрытым ключом.
3. Внедрение заголовка ответа HTTP
Например, если приложение ограничивает управляемый URL-адрес в параметрах jku или x5c, мы могли бы использовать уязвимость внедрения заголовка ответа, чтобы добавить встроенный JWKS в ответ HTTP и заставить приложение проверять подпись для использования.
4. Прочие уязвимости
Веб-токены JSON – это еще одна форма пользовательского ввода, все параметры в которой должны быть очищены должным образом, иначе это может привести к уязвимостям, таким как:
- LFI
- RCE и другим.
Но это не означает, что приложение по-прежнему безопасно, ведь, если злоумышленник не может подделать JWT, он попытается украсть их с помощью неправильной конфигурации:
- XSS
- CSRF
- CORS и т. д.
Заключение
JSON Web Tokens — популярная и доступная технология. При правильном использовании JWT устраняет распространенные ошибки недостаточной авторизации, позволяет легко и удобно распределять информационные потоки между службами, организовывать единую точку входа для различных служб с одинаковыми учетными данными и даже улучшать производительность службы.
Однако, если JWT используется неправильно, вы можете подвергнуть свою систему значительным рискам, включая компрометацию учетных записей всех пользователей в системе.
Итак, для безопасного использования JWT следует:
- использовать защищенное соединение при передаче токенов;
- не передавать в токенах чувствительные пользовательские данные;
- ограничить время жизни JWT и использовать механизм refresh tokens;
- использовать ключевые фразы большой длины;
- обеспечить периодическую смену ключевой фразы;
- вести на стороне приложения белый список разрешенных алгоритмов подписи;
- в идеальном случае работать строго с одним алгоритмом подписи;
- выбирать хорошо известные и проверенные библиотеки для работы с JWT;
- всегда валидировать и санитизировать полученные от пользователя данные.