9 апреля 2014 в 20:13

Тестирование безопасности клиент-серверного API

2 года назад я выступал на конференции CodeFest с темой «Пентест на стероидах. Автоматизируем процесс» и делал пересказ выступления в качестве статьи. В этом году я с большим удовольствием снова принял участие в конференции и выступил с темой «BlackBox тестирование безопасности клиент-серверного API» и, видимо уже в качестве традиции, также делаю пересказ выступления.



Уязвимости в API встречаются. Правда.

О чем разговор?


Разговор про API, но API бывает разным — API операционной системы, сайта, десктопной программы, да хоть чего. Вкратце — API это обращение к методам чего-либо (например в случае ОС — запись в файл) через определенный метод. И запуск какого-нибудь файла с т.ч. зрения разработки произойдет схожим образом (тоже через API метод ОС), хотя результат выполнения команды совершенно разный. В веб-технологиях многие тоже реализуют API к своим проектам, и если на пальцах: можно отправить сообщение в соц. сети зайдя на сайт, а можно — сформировав специальный HTTP запрос. И при реализации подобного альтернативного использования функционала допускаются (как и везде) ошибки в безопасности. Статья как раз о специфичных ошибках при реализации подобного функционала в веб-проектах.

От интерфейса к API методам


Разберем уязвимость в API на youtube, которая не получила широкой огласки. Youtube запрещает из интерфейса использовать спец. символы типа < и > (которые нужны для html тэгов и проведения атаки XSS) в названии роликов. Но! Если найти API и попробовать изменить название этого же видео, то все пройдет успешно. Как итог: нет, xss не выполнилась на странице youtube. Но выполнилась в письме на gmail (когда приходит письмо с роликом), что еще критичнее.

Выводы? Логично — проблема в разной работе методов. А вот почему она случилась — это более интересный вопрос. Здесь бы я выделил следующее:
  • Практически все большие проекты используют ООП и паттерны подобные MVC. И реализации API должны были просто отнаследоваться от метода в интерфейсе и забрать все его ограничения. Значит или нет подобного шаблона и метод писался с нуля (бред? кто знает) или кастомный «хак» в работе интерфейса;
  • Кастомный хак? Проблема разработчика, конечно. Но еще и тимлида, нужно постоянно доносить до разработчиков, где и что нужно реализовывать. Т.е. четко для всех разобрать извечный тред: где в процессе разработки мы валидируем данные (в модели, сервисе, контроллере, где-то еще). Это последствия проблемы применения сложных паттернов при разработке, которые тимлид то понимает нормально, а разработчики не дотягивают и часть валидаций в проекте в одном месте, часть — в другом. Для API корректно отнаследовались, но получили уязвимость.


Что для тестировщиков?
  1. Нужно вообще забыть всё, что мы знали об интерфейсе (его ограничениях) и тестировать проект с нуля;
  2. Проверять подмену параметров, яркий пример со скрином про FaceBook — из интерфейса нельзя было подменить id отправителя, а вот из API — можно;
  3. Проверять «стандартные» атаки, типа sqli/xss;
  4. Если есть автотесты — то это круто. Можно заменить стандартный пейлоад типа testValue1 на различные спецсимволы типа ', ", >, < и матчить их (ищем XSS).


Сжатие


Часто API начинают разрабатывать для применения для мобильных устройств. И, при реализации, добавляют различные сжатия перед отправкой и разжатие после принятия. И есть старая, бородатая атака (ей ложили различные файлообменники) как ZIP бомбы. Вот вопрос: какого размера при распаковке может достичь архив в 42 кб? 4,5 петабайта. Скачать здесь, и тут. Суть простая — создается файл забитый нулями и сжимается. Поэтому сжатие, точнее распаковка — опасное дело, будьте внимательны.

Зло JSON


Иногда API предоставляется не только для какого-то конечного юзера, а порой и для пересылки данных внутри проекта. Часто это встречается на больших, крупных сайтах с различными доменами. И как-то надо взаимодействовать на стороне клиента между доменами, и тут на помощь приходит JSONP. Это такой JSON с нужными на домене 1, который оборачивается в callback. При обращении на домен 1 юзер отправит свои куки, и можно проверить, авторизован ли он и выдать нужные данные. На втором домене вставляется подобный JS

<script type="application/javascript"
        src="http://server1.example.com/api/getUserInfo?jsonp=parseResponse">
</script>


с уже определенной функцией parseResponse. Но суть в том, что злоумышленник может также вставить на своем домене этот скрипт, определить свой callback и, если там sensetive данные — как-то их использовать. Прекрасный пример использования подобной уязвимости продемонстрирован в статье «Сражаясь с анонимностью».

Криптография


Говоря про API сразу на им приходит подписывание запросов. Так как доступ к API часто выдается различным пользователям нужна схема их идентификации. И чаще всего используют следующую схему: каждому выдается свой API ключ, которым разработчик подписывает свои запросы, как-то так:

sign = sha*(... + DATA + ...)

Data — отправляемые данные, вместо многоточия API ключ.
И вопрос, где ставить ключ, слева или справа? Только справа (мы говорим про подписывание запросов именно с применением подобной, очень популярной схемы). Почему? Существует атака на расширение строки. Давайте подробнее.
Представим, что у нас есть данные A=1&B=2&C=3, подпись от них 07ce36c769ae130708258fb5dfa3d37ca5a67514, подпись идет по ошибочной схеме sign=sha1(KEY+DATA).
А теперь ситуация: кто-то перехватил буквально один запрос от клиента к серверу и теперь хочет поменять данные в запросе, но ему потребуется новая подпись, а ключ для подписи не передается (оно и логично). Что он знает? Оригинальные данные и их подпись. Вкратце: есть техническая возможность создать расширить строку (дописать свои данные) и сделать новую подпись (хэш), не зная N байт в начале. На практике новые данные с отступом N байт при хэшировании в начале выглядят так: A=1&B=2&C=3\x80\x00\x00…\x02&C=4 . где
  • A=1&B=2&C=3 — изначальные данные
  • \x80\x00\x00…\x02 — спец. байты для «отступа» (ключ для подписи) при хэшировании
  • &c=4 — наши новые данные



При отправке одного и того же параметра например PHP — возьмет второй. Как раз то, что и нужно для атаки. Так как на первый мы не можем повлиять

Зло победило: можно подписывать запросы, менять параметры не зная ключа для подписи. А вот выдержка из документации по работе с API VK и Mail.RU

Vkontakte: sig = md5(name1=value1name2=value2api_secret) 
Mail.RU sig = md5(uid + params + private_key)

Как видим — ключ справа.

Я обмолвился про кражу всего одного запроса. На практике это бывает чуть проще, чем кажется (история #1, статья)

Небезопасный XML


Думаю каждый встречался с XML, ничем не примечательный тип данных
<recipe name="хлеб" preptime="5" cooktime="180">
  <title>Простой хлеб</title>
  <composition>
    <ingredient amount="3" unit="стакан">Мука</ingredient>
  </composition>
...
</recipe>


Но уже менее людей встречались с сущностями в XML

DTD Example:
<!ENTITY writer "Donald Duck."> 
<!ENTITY copyright "Copyright W3Schools."> 


XML example:
<author>&writer;©right;</author> 


Которые позволяют что-то определить, а потом повторно использовать. На примере выше — просто строки.
А еще меньше людей сталкивались с внешними XML сущностями…

<!DOCTYPE foo [ 
<!ELEMENT foo ANY > 
<!ENTITY xxe SYSTEM 
"file:///etc/passwd" >]> 
 
<foo>&xxe;</foo>

В качестве сущности может выступить что-то внешнее, локальный файл, внешний файл по http и т.д. Т.е. это стандарт. Изначально задумывался в благих целях, например нужно отдать XMLку клиенту и взять текущее время с другого сервера (другого xml файла, доступного по HTTP).

Долго не рассуждая, уязвимость очевидна. Используя различные врапперы — file:///, http:// атакующий может получать информацию о системе, сети и т.п. Иногда это может привести и к удаленному выполнению команд, например в PHP существует враппер expect://, который сначала выполняет команду на ОС и возвращает результат её работы. Так что по-умолчанию этой атаке подвержены все стандартные парсеры XML. Одно из решений приходит таким: отключить поддержку внешних сущностей. Тогда атакущий может применить XML бомбу:

<?xml version="1.0"?>
<!DOCTYPE lolz [
 <!ENTITY lol "lol">
 <!ELEMENT lolz (#PCDATA)>
 <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
 <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
 <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
 <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
 <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
 <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
 <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
 <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
 <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>


И «положить» сервер. Суть в постоянной пересылке сущности на сущность.
Стоит помнить, что это может быть проблема не только сервера, но и клиента (например дектопное ПО клиента парсит XML).
Самый яркий пример использования уязвимости — взлом FaceBook в ноябре 2013, который мог повлечь и выполнение команд от ОС.

Резюмируя:
  • В первую очередь перетестировать все «интересные» ограничения интерфейса;
  • Разобраться со сжатием;
  • Внимательно следит за JS callbac'ами;
  • Криптография. Может быть не только атака на расширение строки, а просто слабый (например — короткий) ключ, или как-то предсказуемый.
  • А еще можно встретить просто зашитые в мобильное приложение API данные. Кстати, их можно найти через сервис http://hackapp.com;
  • XML — XXE;
  • Что угодно ещё.


Презентация:


Демо видео sha padding и XXE:


upd 25/04/0214: Запись доклада
Автор: @BeLove
Digital Security
рейтинг 221,72
Безопасность как искусство

Комментарии (16)

  • –4
    В отношени youtube. Это, вроде как, логично — проверять корректность названия ролика на уровне UI, на клиенте, при помощи Javacript и только потом отправлят запрос на сервер. В случае с API на клиенте-то нечем проверить, надо было добавить проверку и на сервере?
    • +3
      Да
    • 0
      Более того, бэк-энд еще и ошибки должен отдавать в случае невалидных данных.
  • 0
    Зло JSON

    Иногда API предоставляется не только для какого-то конечного юзера, а порой и для пересылки данных внутри проекта. Часто это встречается на больших, крупных сайтах с различными доменами. И как-то надо взаимодействовать на стороне клиента между доменами, и тут на помощь приходит JSONP. Это такой JSON с нужными на домене 1, который оборачивается в callback. При обращении на домен 1 юзер отправит свои куки, и можно проверить, авторизован ли он и выдать нужные данные. На втором домене вставляется подобный JS


    Абсолютно не понял, причём здесь JSON(P). Это просто вид CSRF-атаки, и защищаться от неё нужно CSRF-токеном.
    • 0
      При том, что когда разработчики реализуют jsonp для кроссдоменной работы (как в примере выше, некоторое внутренее API) как раз и допускают и ошибку, что отсутствуют какие-либо токены (о которых обычно нигде не говорится).
      • 0
        Это всего лишь подвид уязвимости, не самый частый и опасный.
        • 0
          Найти подписывание с ключом справа — тоже не так часто можно встретить.
          Моя задача была — дать более-менее универсальный обзор ошибок при разработке API. При этом случай с jsonp — один из популярных, особенно в «западных» проектах.
          • 0
            CSRF в целом гораздо опаснее и популярнее, чем его jsonp-подвид. Странно, что вы решили его (CSRF) даже не упоминать.
            • 0
              Где CSRF популярен, в API?
              • 0
                Ага.
                • 0
                  Раскройте полностью мысль, приведите примеры, ибо API в веб может быть абсолютно разным. Если говорить про API с подписыванием каждого запроса — о каком CSRF вообще может идти речь?
                  Иначе комментарии больше похожи на троллинг и провокации, нежели чем на что-то конструктивное.
                  • +1
                    Перечитал комментарии, не заметил троллинга и провокации.
                    Те же самые внутренние API, о которых вы говорите в разделе «Зло JSON» часто имеют ручки, выполняющие некоторые действия. Эти ручки часто не защищены токенами, либо их авторы предполагают, что перевода их на POST вместо GET достаточно для обеспечения безопасности. Это гигантская дыра, которую можно найти почти в любом крупном проекте.

                    CSRF, строго говоря, это возможность выполнить некое действие «от имени пользователя», заманив его сайт злоумышленника, откуда JS-ом или сабмитом форм в iframe будет выполняться специально сгенерированный запрос к API сервиса. Раскрытие информации через jsonp — один из вариантов использования незащищенной CSRF-ручки — разумеется, не единственный. Наиболее типичные и опасные случаи — это выполнение некоторых действий от имени пользователя (постинг сообщений, например).
                    • 0
                      Спасибо за развернутый комментарий и ценное дополнение, про подобные случаи согласен.
                      Вообще в ходе подготовки доклада сложно было разделить типичные веб-баги (ведь CSRF всюду, не только в API) и баги именно при реализации API, так что скорее из-за этого опустил этот момент. Про jsonp добавил вообще за день до выступления и долго сомневался, нужно ли.
                      Ну а теперь тот, кто прочтет статью и комменты, узнает чуть больше (:
                    • 0
                      Не, не, вы _немного_ про разное говорите. Собственно одно есть нарушение целостности, а другое конфиденциальности. Да, технически МЕТОД АТАКИ — CSRF, и Токен будет решением. Но с точки зрения ИБ — это немного разные риски и угрозы. Например JSONP возвращает по запросу список сообщений к клиенту. Зачем мне городить токен, если JSON достаточно? Изменить через этот «CSRF» ничего нельзя, только получить, но так как у меня JSON без P, то и украсть нельзя. Какие в итоге у меня угрозы от этого CSRF, который, несомненно есть и зачем вы мне навязываете токен?

                      • 0
                        Фрод и прочие искажения статистики, например.
                        • 0
                          Фрод это как?
                          Искажение статистики запросов? Зачем, для кого? Кто и почему будет встраивать CSRF с такими целями? Цена атаки дороже профита, которого может и не быть

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.

Самое читаемое Разработка