Contents
- 1 Что такое JWT?
- 2 Что такое JWT-атаки?
- 3 Каково влияние JWT-атак?
- 4 Как возникают уязвимости к JWT-атакам?
- 5 Как работать с JWT в Burp Suite
- 6 Использование ошибочной проверки подписи JWT
- 7 Перебор секретных ключей
- 8 Внедрение параметров заголовка JWT
- 9 Путаница в алгоритме JWT
- 10 Как предотвратить атаки JWT
Что такое JWT?
Веб-токены JSON (JWT) — это стандартизированный формат для отправки криптографически подписанных данных JSON между системами. Теоретически они могут содержать любые данные, но чаще всего используются для отправки информации («заявлений») о пользователях в рамках механизмов аутентификации, обработки сеансов и управления доступом.
В отличие от классических токенов сеанса, все данные, необходимые серверу, хранятся на стороне клиента в самом JWT. Это делает JWT популярным выбором для широко распределенных веб-сайтов, где пользователям необходимо беспрепятственно взаимодействовать с несколькими внутренними серверами.
формат JWT
JWT состоит из 3 частей: заголовка , полезной нагрузки и подписи . Каждый из них разделен точкой, как показано в следующем примере:
eyJraWQiOiI5MTM2ZGRiMy1jYjBhLTRhMTktYTA3ZS1lYWRmNWE0NGM4YjUiLCJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJwb3J0c3dpZ2dlciIsImV4cCI6MTY0ODAzNzE2NCwibmFtZSI6IkNhcmxvcyBNb250b3lhIiwic3ViIjoiY2FybG9zIiwicm9sZSI6ImJsb2dfYXV0aG9yIiwiZW1haWwiOiJjYXJsb3NAY2FybG9zLW1vbnRveWEubmV0IiwiaWF0IjoxNTE2MjM5MDIyfQ.SYZBPIBg2CRjXAJ8vCER0LA_ENjII1JakvNQoP-Hw6GG1zfl4JyngsZReIfqRvIAEi5L4HV0q7_9qGhQZvy9ZdxEJbwTxRs_6Lb-fZTDpW6lKYNdMyjw45_alSCZ1fypsMWz_2mTpQzil0lOtps5Ei_z7mM7M8gCwe_AGpI53JxduQOaB5HkT5gVrv9cKu9CsW5MS6ZbqYXpGyOG5ehoxqm8DL5tFYaW3lB50ELxi0KsuTKEbD0t5BCl0aCR2MBJWAbN-xeLwEenaqBiwPVvKixYleeDQiBEIylFdNNIMviKRgXiYuAvMziVPbwSgkZVHeEdF5MQP1Oe2Spac-6IfA
Части заголовка и полезной нагрузки JWT — это просто объекты JSON с кодировкой base64url. Заголовок содержит метаданные о самом токене, а полезная нагрузка содержит фактические «утверждения» о пользователе. Например, вы можете декодировать полезную нагрузку из токена выше, чтобы выявить следующие утверждения:
{
"iss": "portswigger",
"exp": 1648037164,
"name": "Carlos Montoya",
"sub": "carlos",
"role": "blog_author",
"email": "carlos@carlos-montoya.net",
"iat": 1516239022
}
В большинстве случаев эти данные может легко прочитать или изменить любой, у кого есть доступ к токену. Поэтому безопасность любого механизма на основе JWT сильно зависит от криптографической подписи.
JWT-подпись
Сервер, выдающий токен, обычно создает подпись, хэшируя заголовок и полезные данные. В некоторых случаях они также шифруют полученный хэш. В любом случае, этот процесс включает в себя секретный ключ подписи. Этот механизм позволяет серверам проверить, что ни один из данных в токене не был изменен с момента его выпуска:
- Поскольку подпись напрямую получена из остальной части маркера, изменение одного байта заголовка или полезной нагрузки приводит к несоответствию подписи.
- Не зная секретного ключа подписи сервера, невозможно сгенерировать правильную подпись для заданного заголовка или полезной нагрузки.
Кончик
Если вы хотите лучше понять, как устроены JWT, вы можете использовать отладчик jwt.io
для экспериментов с произвольными токенами.
JWT против JWS против PLAY
Спецификация JWT на самом деле очень ограничена. Он определяет только формат представления информации («утверждений») в виде объекта JSON, который может передаваться между двумя сторонами. На практике JWT не используются как автономные сущности. Спецификация JWT расширена спецификациями JSON Web Signature (JWS) и JSON Web Encryption (JWE), которые определяют конкретные способы фактической реализации JWT.
Другими словами, JWT обычно представляет собой токен JWS или JWE. Когда люди используют термин «JWT», они почти всегда имеют в виду токен JWS. JWE очень похожи, за исключением того, что фактическое содержимое токена зашифровано, а не просто закодировано.
Примечание
Для простоты в этих материалах «JWT» относится в первую очередь к токенам JWS, хотя некоторые из описанных уязвимостей могут относиться и к токенам JWE.
Что такое JWT-атаки?
В атаках JWT пользователь отправляет модифицированные JWT на сервер для достижения злонамеренной цели. Как правило, эта цель состоит в том, чтобы обойти проверку подлинности и контроль доступа , выдавая себя за другого пользователя, который уже прошел проверку подлинности.
Каково влияние JWT-атак?
Воздействие JWT-атак обычно серьезное. Если злоумышленник может создать свои собственные действительные токены с произвольными значениями, он может повысить свои собственные привилегии или выдать себя за других пользователей, получив полный контроль над своими учетными записями.
Как возникают уязвимости к JWT-атакам?
Уязвимости JWT обычно возникают из-за неправильной обработки JWT в самом приложении. Различные спецификации , связанные с JWT, относительно гибки по своей структуре, что позволяет разработчикам веб-сайтов самостоятельно решать многие детали реализации. Это может привести к тому, что они случайно внесут уязвимости даже при использовании закаленных в боях библиотек.
Эти недостатки реализации обычно означают, что подпись JWT не проверяется должным образом. Это позволяет злоумышленнику подделывать значения, передаваемые приложению через полезную нагрузку токена. Даже если подпись тщательно проверена, можно ли ей действительно доверять, в значительной степени зависит от того, останется ли секретный ключ сервера секретным. Если этот ключ каким-то образом утек, или его можно угадать или подобрать методом грубой силы, злоумышленник может сгенерировать действительную подпись для любого произвольного токена, скомпрометировав весь механизм.
Как работать с JWT в Burp Suite
Если вы раньше не работали с JWT, мы рекомендуем ознакомиться с соответствующими функциями Burp Suite, прежде чем приступать к лабораторным работам в этой теме.
Использование ошибочной проверки подписи JWT
По своей конструкции серверы обычно не хранят никакой информации о выдаваемых ими JWT. Вместо эЧитать далее
Атаки с путаницей алгоритма JWTтого каждый токен является полностью автономным объектом. Это имеет несколько преимуществ, но также создает фундаментальную проблему — сервер на самом деле ничего не знает об исходном содержимом токена или даже о том, какой была исходная подпись. Следовательно, если сервер не проверит подпись должным образом, ничто не помешает злоумышленнику внести произвольные изменения в остальную часть токена.
Например, рассмотрим JWT, содержащий следующие утверждения:
{
"username": "carlos",
"isAdmin": false
}
Если сервер идентифицирует сеанс на основе этого username
, изменение его значения может позволить злоумышленнику выдавать себя за других вошедших в систему пользователей. Точно так же, если isAdmin
значение используется для управления доступом, это может предоставить простой вектор для повышения привилегий.
В первых двух лабораторных работах вы увидите несколько примеров того, как эти уязвимости могут выглядеть в реальных приложениях.
Принятие произвольных подписей
Библиотеки JWT обычно предоставляют один метод для проверки токенов, а другой просто их декодирует. Например, в библиотеке Node.js jsonwebtoken
есть verify()
и decode()
.
Иногда разработчики путают эти два метода и передают decode()
методу только входящие токены. Фактически это означает, что приложение вообще не проверяет подпись.
Прием токенов без подписи
Помимо прочего, заголовок JWT содержит alg
параметр. Это сообщает серверу, какой алгоритм использовался для подписи токена и, следовательно, какой алгоритм необходимо использовать при проверке подписи.
{
"alg": "HS256",
"typ": "JWT"
}
Это по своей сути ошибочно, потому что у сервера нет другого выбора, кроме как неявно доверять управляемому пользователем вводу из токена, который на данный момент вообще не проверен. Другими словами, злоумышленник может напрямую влиять на то, как сервер проверяет, заслуживает ли токен доверия.
JWT могут быть подписаны с использованием ряда различных алгоритмов, но также могут быть оставлены без подписи. В данном случае alg
параметру присваивается значение none
, что указывает на так называемый «незащищенный JWT». Из-за очевидных опасностей серверы обычно отклоняют токены без подписи. Однако, поскольку этот тип фильтрации основан на анализе строк, иногда вы можете обойти эти фильтры, используя классические методы запутывания, такие как смешанные заглавные буквы и неожиданные кодировки.
Примечание
Даже если токен не подписан, часть полезной нагрузки все равно должна заканчиваться точкой в конце.
Перебор секретных ключей
Некоторые алгоритмы подписи, такие как HS256 (HMAC + SHA-256), используют в качестве секретного ключа произвольную автономную строку. Как и в случае с паролем, важно, чтобы этот секрет не мог быть легко угадан или взломан злоумышленником. В противном случае они могут создавать JWT с любыми значениями заголовка и полезной нагрузки, которые им нравятся, а затем использовать ключ для повторной подписи токена с действительной подписью.
При реализации JWT-приложений разработчики иногда допускают ошибки, например забывают изменить секреты по умолчанию или заполнители. Они могут даже копировать и вставлять фрагменты кода, найденные в Интернете, а затем забыть изменить жестко заданный секрет, приведенный в качестве примера. В этом случае злоумышленнику может быть тривиально взломать секрет сервера, используя список известных секретов .
Перебор секретных ключей с помощью hashcat
Мы рекомендуем использовать hashcat для перебора секретных ключей. Вы можете установить hashcat вручную , но он также предустановлен и готов к использованию в Kali Linux.
Примечание
Если вы используете предварительно созданный образ VirtualBox для Kali, а не версию установщика на «голом железе», может не хватить памяти для запуска hashcat.
Вам просто нужен действующий подписанный JWT с целевого сервера и список известных секретов . Затем вы можете запустить следующую команду, передав JWT и список слов в качестве аргументов:
hashcat -a 0 -m 16500 <jwt> <wordlist>
Hashcat подписывает заголовок и полезную нагрузку из JWT, используя каждый секрет в списке слов, а затем сравнивает полученную подпись с исходной с сервера. Если какая-либо из подписей совпадает, hashcat выводит идентифицированный секрет в следующем формате вместе с другими деталями:
<jwt>:<identified-secret>
Примечание
Если вы запускаете команду более одного раза, вам необходимо включить --show
флаг для вывода результатов.
Поскольку hashcat работает локально на вашем компьютере и не полагается на отправку запросов на сервер, этот процесс выполняется очень быстро, даже при использовании огромного списка слов.
После того, как вы определили секретный ключ, вы можете использовать его для создания действительной подписи для любого заголовка JWT и полезных данных, которые вам нравятся. Подробнее о том, как повторно подписать измененный JWT в Burp Suite, см . в разделе Подписание JWT .
Внедрение параметров заголовка JWT
Согласно спецификации JWS alg
обязательным является только параметр заголовка. Однако на практике заголовки JWT (также известные как заголовки JOSE) часто содержат несколько других параметров. Следующие из них представляют особый интерес для злоумышленников.
jwk
(Веб-ключ JSON) — предоставляет встроенный объект JSON, представляющий ключ.jku
(URL-адрес набора веб-ключей JSON) — предоставляет URL-адрес, с которого серверы могут получить набор ключей, содержащих правильный ключ.kid
(Идентификатор ключа) — предоставляет идентификатор, который серверы могут использовать для определения правильного ключа в случаях, когда на выбор предлагается несколько ключей. В зависимости от формата ключа он может иметь соответствующийkid
параметр.
Как видите, каждый из этих контролируемых пользователем параметров сообщает серверу-получателю, какой ключ использовать при проверке подписи. В этом разделе вы узнаете, как использовать их для внедрения модифицированных JWT, подписанных с использованием вашего собственного произвольного ключа, а не секрета сервера.
Внедрение самоподписанных JWT через параметр jwk
Спецификация веб-подписи JSON (JWS) описывает необязательный jwk
параметр заголовка, который серверы могут использовать для встраивания своего открытого ключа непосредственно в сам токен в формате JWK.
ЮВК
JWK (веб-ключ JSON) — это стандартизированный формат для представления ключей в виде объекта JSON.
Вы можете увидеть пример этого в следующем заголовке JWT:
{
"kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
"typ": "JWT",
"alg": "RS256",
"jwk": {
"kty": "RSA",
"e": "AQAB",
"kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
"n": "yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9m"
}
}
Открытые и закрытые ключи
Если вы не знакомы с терминами «открытый ключ» и «закрытый ключ», мы рассмотрели это как часть наших материалов об атаках с путаницей алгоритмов. Дополнительные сведения см. в разделе Симметричные и асимметричные алгоритмы .
В идеале серверы должны использовать только ограниченный белый список открытых ключей для проверки подписей JWT. Однако неправильно настроенные серверы иногда используют любой ключ, встроенный в jwk
параметр.
Вы можете воспользоваться этим поведением, подписав измененный JWT, используя свой собственный закрытый ключ RSA, а затем внедрив соответствующий открытый ключ в jwk
заголовок.
Хотя вы можете вручную добавить или изменить jwk
параметр в Burp, расширение JWT Editor предоставляет полезную функцию, которая поможет вам протестировать эту уязвимость:
- Загрузив расширение, на главной панели вкладок Burp перейдите на вкладку JWT Editor Keys .
- Создайте новый ключ RSA.
- Отправьте запрос, содержащий JWT, на Burp Repeater.
- В редакторе сообщений перейдите на вкладку JSON Web Token , созданную расширением, и измените полезные данные токена по своему усмотрению.
- Нажмите « Атака », затем выберите «Встроенный JWK» . При появлении запроса выберите только что сгенерированный ключ RSA.
- Отправьте запрос, чтобы проверить реакцию сервера.
Вы также можете выполнить эту атаку вручную, добавив jwk
заголовок самостоятельно. Однако вам также может потребоваться обновить kid
параметр заголовка JWT, чтобы он соответствовал kid
встроенному ключу. Встроенная атака расширения позаботится об этом шаге за вас.
Внедрение самоподписанных JWT через параметр jku
Вместо встраивания открытых ключей напрямую с помощью jwk
параметра заголовка некоторые серверы позволяют использовать jku
параметр заголовка (JWK Set URL) для ссылки на набор JWK, содержащий ключ. При проверке подписи сервер извлекает соответствующий ключ из этого URL-адреса.
Набор JWK
Набор JWK — это объект JSON, содержащий массив JWK, представляющих разные ключи. Вы можете увидеть пример этого ниже.
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"kid": "75d0ef47-af89-47a9-9061-7c02a610d5ab",
"n": "o-yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9mk6GPM9gNN4Y_qTVX67WhsN3JvaFYw-fhvsWQ"
},
{
"kty": "RSA",
"e": "AQAB",
"kid": "d8fDFo-fS9-faS14a9-ASf99sa-7c1Ad5abA",
"n": "fc3f-yy1wpYmffgXBxhAUJzHql79gNNQ_cb33HocCuJolwDqmk6GPM4Y_qTVX67WhsN3JvaFYw-dfg6DH-asAScw"
}
]
}
Подобные наборы JWK иногда публикуются публично через стандартную конечную точку, например /.well-known/jwks.json
.
Более безопасные веб-сайты будут извлекать ключи только из доверенных доменов, но иногда вы можете воспользоваться расхождениями в синтаксическом анализе URL-адресов, чтобы обойти этот тип фильтрации. Мы рассмотрели некоторые из них в нашей теме по SSRF .
Серверы могут использовать несколько криптографических ключей для подписи различных типов данных, а не только JWT. По этой причине заголовок JWT может содержать kid
параметр (ID ключа), который помогает серверу определить, какой ключ использовать при проверке подписи.
Ключи проверки часто хранятся в виде набора JWK. В этом случае сервер может просто искать JWK с таким же kid
токеном. Однако спецификация JWS не определяет конкретной структуры для этого идентификатора — это просто произвольная строка по выбору разработчика. Например, они могут использовать этот kid
параметр, чтобы указать на конкретную запись в базе данных или даже на имя файла.
Если этот параметр также уязвим для обхода каталога , злоумышленник потенциально может заставить сервер использовать произвольный файл из своей файловой системы в качестве ключа проверки.
{
"kid": "../../path/to/file",
"typ": "JWT",
"alg": "HS256",
"k": "asGsADas3421-dfh9DGN-AFDFDbasfd8-anfjkvc"
}
Это особенно опасно, если сервер также поддерживает JWT, подписанные с использованием симметричного алгоритма . В этом случае злоумышленник потенциально может указать kid
параметр на предсказуемый статический файл, а затем подписать JWT, используя секрет, соответствующий содержимому этого файла.
Теоретически вы можете сделать это с любым файлом, но один из самых простых способов — использовать /dev/null
, который присутствует в большинстве систем Linux. Поскольку это пустой файл, при его извлечении возвращается значение null. Таким образом, подписание маркера нулевым байтом в кодировке Base64 приведет к получению действительной подписи.
Если сервер хранит свои ключи проверки в базе данных, kid
параметр заголовка также является потенциальным вектором для атак путем внедрения кода SQL .
Другие интересные параметры заголовка JWT
Также злоумышленникам могут быть интересны следующие параметры заголовка:
cty
(Тип контента) — иногда используется для объявления типа мультимедиа для контента в полезной нагрузке JWT. Обычно это не указывается в заголовке, но базовая библиотека синтаксического анализа все равно может его поддерживать. Если вы нашли способ обойти проверку подписи, вы можете попробовать внедритьcty
заголовок, чтобы изменить тип контента наtext/xml
илиapplication/x-java-serialized-object
, что потенциально может открыть новые векторы для атак XXE и десериализации .x5c
(Цепочка сертификатов X.509) — иногда используется для передачи сертификата открытого ключа X.509 или цепочки сертификатов ключа, используемого для цифровой подписи JWT. Этот параметр заголовка можно использовать для внедрения самозаверяющих сертификатов, аналогично атакам сjwk
внедрением заголовков, которые обсуждались выше. Из-за сложности формата X.509 и его расширений синтаксический анализ этих сертификатов также может привести к уязвимостям. Подробности этих атак выходят за рамки этих материалов, но для более подробной информации ознакомьтесь с CVE-2017-2800 и CVE-2018-2633 .
Путаница в алгоритме JWT
Даже если сервер использует надежные секреты, которые вы не можете взломать, вы все равно сможете подделать действительные JWT, подписав токен с помощью алгоритма, которого разработчики не ожидали. Это известно как атака с путаницей алгоритмов.
Как предотвратить атаки JWT
Вы можете защитить свои веб-сайты от многих рассмотренных нами атак, приняв следующие меры высокого уровня:
- Используйте актуальную библиотеку для обработки JWT и убедитесь, что ваши разработчики полностью понимают, как она работает, а также какие-либо последствия для безопасности. Современные библиотеки затрудняют их непреднамеренное небезопасное внедрение, но это не является надежным из-за гибкости, присущей соответствующим спецификациям.
- Убедитесь, что вы выполняете надежную проверку подписи для любых JWT, которые вы получаете, и учитываете пограничные случаи, такие как JWT, подписанные с использованием неожиданных алгоритмов.
- Ввести строгий белый список разрешенных хостов для
jku
заголовка. - Убедитесь, что вы не уязвимы для обхода пути или внедрения SQL через
kid
параметр заголовка.
Дополнительные рекомендации по обработке JWT
Хотя это и не обязательно, чтобы избежать появления уязвимостей, мы рекомендуем придерживаться следующих рекомендаций при использовании JWT в ваших приложениях:
- Всегда устанавливайте дату истечения срока действия для любых токенов, которые вы выпускаете.
- По возможности избегайте отправки токенов в параметрах URL.
- Включите утверждение
aud
(аудитория) (или подобное), чтобы указать предполагаемого получателя токена. Это предотвращает его использование на разных веб-сайтах. - Разрешить серверу-эмитенту отозвать токены (например, при выходе из системы).
Статья является переводом portswigger.net