
Салют, Хабр! Меня зовут Всеволод, и я занимаюсь анализом защищенности веб-приложений в Positive Technologies.
С API веб-приложений я успел познакомиться со всех сторон: как разработчик, инженер в AppSec и пентестер. В большой корпорации мне пришлось столкнуться с колоссальными объемами API. Я быстро осознал, что в таких количествах их просто невозможно проверить вручную, и начал искать способы автоматизации. В результате уже больше двух лет я занимаюсь динамическим тестированием (DAST), в частности фаззингом.
В этой статье я расскажу, почему считаю DAST не менее важным, чем статический анализ кода (SAST), как новичку начать фаззить, а опытному специалисту научиться находить еще больше уязвимостей. В основе этой статьи материал моего выступления на PHDays Fest в треке Development Security. Если вам больше нравится видео, можно посмотреть его на vkvideo или на youtube.
Что скрывает API
С точки зрения безопасности API — это не просто интерфейс, который позволяет программам взаимодействовать друг с другом и обмениваться данными. Это еще и область риска, ведь в программных интерфейсах встречаются всевозможные уязвимости, например мисконфигурации. Зачастую они возникают из-за халатности разработчиков, которые не обрабатывают ошибки, связанные с неправильной настройкой API, или используют стандартные настройки для сторонних фреймворков вместо того, чтобы адаптировать их под конкретный проект.
В API могут встречаться и критические уязвимости, такие как сломанный контроль доступа, позволяющий злоумышленнику повысить свои привилегии в системе и похитить конфиденциальные данные. Еще один популярный сценарий — инъекции или, иными словами, внедрение вредоносного кода, которое может сопровождаться выполнением произвольных команд.
Чем больше масштаб приложения, тем богаче его API и тем чаще оно (приложение) обновляется. В результате поверхность (много слова возможно) атаки расширяется, у злоумышленников появляется больше возможностей, а специалистам по ИБ становится сложнее защищать инфраструктуру.
Статика или динамика: что выбрать
Выбирая метод для тестирования приложения, многие специалисты сразу отбрасывают DAST. Мол, SAST позволяет сканировать исходный код, а больше ничего и не нужно. На деле же SAST способен находить далеко не все пробелы в безопасности.
Давайте посмотрим, например, как популярный статический анализатор Semgrep, справляется с разными типами уязвимостей. Первая в очереди — простая SQL-инъекция в форматной строке.

Как мы видим, Semgrep хватило правил из коробки, чтобы обнаружить уязвимость.
Усложним задачу: возьмем код, в котором разработчик проявил креативность и преобразовал SQL-запрос в буфер.

Правилами анализатора такое, конечно, не было предусмотрено, и Semgrep провалил задачу.
В тоже время при использовании DAST изыски внутренней реализации приложения не мешают обнаруживать подобные уязвимости. Отправив данные с нагрузкой для внедрения в SQL-запрос, мы получим ошибку в ответе сервера.
Отсюда вывод: SAST не заменяет DAST, и для тестирования безопасности приложений необходимы оба подхода.
Как работает фаззинг API
Фаззинг — одна из техник, используемых в DAST. Это метод выявления уязвимостей, при котором приложению преднамеренно посылаются случайные, неожиданные или неправильные данные. В основе лежит простая концепция: мутация входных данных влияет на ответы приложения. Например, если в программе Postman – мощном и интуитивно понятном инструменте, предназначенном специально для тестирования и разработки API, ввести предусмотренное разработчиком значение, ничего не произойдет. Но стоит добавить к нему нагрузочные данные, в нашем случае это SQL-синтаксис, как система тут же выдаст ошибку.

Чем больше будет таких проверок, тем больше уязвимостей удастся найти. Идеальный сценарий — тестирование всех конечных точек всеми возможными методами, с различными параметрами и нагрузкой. На качество тестирования влияет и то, как мы определяем, что приложение уязвимо: делать выводы можно не только по сообщению, что в ответе ошибка, но и по статус-кодам, по размеру ответа и другим параметрам.
Весь процесс фаззинга можно пройти в четыре шага:
Развертываем stage-окружение. Лучше создать отдельную среду для тестирования: если начать фаззить прод и случайно его уронить, то по голове нас точно не погладят. При этом, чтобы сработки были релевантными, stage-окружение должно максимально соответствовать рабочей версии приложения.
Собираем данные. Стоит заранее решить, какие API-ручки и параметры вы будете тестировать. Где их взять, обсудим чуть дальше.
Настраиваем проверки. Определитесь, какие типы уязвимостей вы будете искать и какие нагрузочные данные использовать. На этом этапе стоит обратить внимание на модель угроз компании и стек используемых технологий. Например, нет смысла искать NoSQL-инъекцию в PostgreSQL.
Запускаем фаззинг. Для него существуют и готовые инструменты, о них также сегодня поговорим.
Где искать данные
Прежде чем приступить к фаззингу, нужно тщательно изучить конечные точки приложения. Для этого необходимо собрать данные об используемых HTTP-методах, параметрах и формате значений, ожидаемых на валидации. Источников этой информации множество.
OpenAPI
С этим форматом описания HTTP, думаю, знакомы многие. Как правило, его используют разработчики, чтобы другие специалисты могли изучать взаимодействие с API их приложения. Помимо внутренней разработки, OpenAPI может быть полезен при аудите информационной безопасности.
Где данные. GitHub, GitLab, пути /docs, /openapi, /swagger.
Форматы. YAML, JSON.
Аналог. Swagger.

Postman-коллекции
Чуть менее очевидный, но тоже популярный источник. Разработчики используют Postman, чтобы тестировать API и создавать приложения по API-first подходам.
Где данные. GitHub, GitLab, Wiki конкретного проекта.
Формат. JSON.
Аналоги. Insomnia, Hoppscotch.

Burp Suite
Если данных из OpenAPI или Postman недостаточно, можно самостоятельно собрать трафик. Для этого запускаем исследуемое приложение и проксируем все его запросы через Burp Suite. Важно направить как можно больше запросов: пройти аутентификацию, просмотреть все страницы. Результат Burp сохранит как историю, которую потом можно легко экспортировать в виде XML-файла.
Где данные. Запросы изучаемого приложения.
Формат. XML.
Аналог. XML OWASP ZAP.

Proxify
Аналог Burp Suite, работающий в формате интерфейса командной строки (Command Line Interface, CLI). Proxify отлично подойдет в качестве промежуточного прокси-сервера для автоматизации сбора запросов, в том числе чужих.
Где данные. Запросы изучаемого приложения.
Форматы. JSONL, YAML.
Аналог. Mitmproxy.

Журналы веб-серверов
Использовать журналы веб-сервера, или логи, — более сложный, но не менее интересный путь. Разрабатывая приложение, крупная компания логирует запросы, чтобы оценивать, как программа ведет себя под нагрузкой и почему возникают ошибки. Логи могут дать всю необходимую для фаззинга информацию. Например, популярный инструмент Elasticsearch позволяет получить интересующие данные о запросах к веб-приложению за определенный временной срез.
Где данные. Инфраструктура компании, окружение приложения.
Формат. JSON.
Аналоги. Внутренние системы сбора и хранения журналов.
Yandex-tank
Напоследок самый нетривиальный способ сбора информации — это взаимодействие с командами нагрузочного тестирования. Данные можно позаимствовать у QA-инженеров, которые с их помощью тестируют API веб-приложения. Например, у них могут быть подготовлены сырые HTTP-запросы, так называемые патроны (ammo), для инструмента Yandex-tank.
Где данные. Наработки команд тестирования.
Формат. Raw HTTP request.
Аналоги. Внутренние реализации нагрузочного тестирования.

Фаззинг для новичков: сканер Nuclei
Тем, кто никогда раньше не занимался фаззингом, я советую начать со сканера Nuclei. Это инструмент для проверки безопасности приложений с открытым исходным кодом от команды Project Discovery. Вот почему я считаю его идеальным для новичков:
· Подробная документация. У проекта большое комьюнити, которое постоянно растет, увеличивая число проверок.
· Готовые шаблоны для уязвимостей. Уже отработаны на большом количестве приложений.
· Встроенные возможности для фаззинга. Получив карту приложения, составленную по информации из источников, сканер таргетировано проверяет каждый параметр по принципу декартового произведения: использует нагрузочные данные для каждой конечной точки, каждого метода и параметра.
· Поддержка различных форматов значений. OpenAPI, Burp Suite, JSONL, YAML multidoc.
Шаблон проверки для Nuclei позволяет настроить анализ критериев, определяющих наличие уязвимостей, задать нагрузочные данные и настроить параметры фаззинга, например, чтобы тестировать необходимые части веб-приложения. Не буду вдаваться в подробности настроек Nuclei — все подробно расписано в документации проекта.

При всей универсальности Nuclei и здесь не обходится без сложностей: не все источники подойдут для прямого запуска в сканере. Так, если Postman-коллекцию всегда можно преобразовать в спецификацию OpenIPA в самом Postman, то для работы с журналами веб-серверов (Elasticsearch) и данными нагрузочного тестирования (Yandex-tank) потребуется нормализация данных в виде промежуточного «перекладывателя» JSON в подходящий для Nuclei формат.
Фаззинг для продвинутых: методы и лайфхаки
Если вы уже уверенно проводите фаззинг и хотите находить больше уязвимостей, в том числе нестандартных, процесс можно усовершенствовать.
Пишите собственные шаблоны для уязвимостей. Они могут учитывать уникальные особенности вашего приложения, которые не отражены в стандартных шаблонах Nuclei или других сканеров. Например, я встречался с приложениями, которые вне зависимости от того произошла ли при обработке запроса ошибка отвечают с кодом 200, но результат запроса, в том числе реально описывающий ситуацию статус код возвращают в теле ответа. Для многих сканеров уязвимостей с настройками по умолчанию это непреодолимое препятствие.
Внешние (out-of-band) интеракции. Взаимодействие с внешними элементами системы, которое невозможно отследить со стороны отправляющего запрос, может быть одним из факторов уязвимого поведения приложения. Для мониторинга подобных ситуаций можно внедрить свой сервис и попытаться спровоцировать обращение тестируемого приложения к нему. У создателей Nuclei – команды Project Discovery есть готовый инструмент с интеграцией в Nuclei – Interactsh.
Создавайте отчеты. Работать прямо в консольке, может быть, и лампово, но если у вас много данных, то их нужно где-то хранить и делать удобные отчеты. Я советую выгружать информацию из Nuclei в более удобном формате, например SARIF или Markdown.
Используйте пайплайны CI/СD. Например, можно сделать так, чтобы при каждом значительном обновлении приложения автоматически разворачивалась тестовая среда, а рядом запускался контейнер Nuclei. Пример для GitHub Actions также есть у команды Project Discovery.
Автоматизируйте сбор данных. Попробуйте перехватить чужой трафик с помощью прокси-сервера и загрузить его в Nuclei для анализа. Другой вариант: использовать специальную программу, которая соберет данные из сторонней системы логирования или нагрузочного тестирования.
Запускайте собственные движки сканирования. Это вариант для самых продвинутых. Если возможностей Nuclei вам не хватает, напишите для него собственные обертки либо создайте сканер с нуля.
Вместо постскриптума
Что ж, осталось только сказать, что фаззинг API — это совсем не сложно, хотя бы потому, что в нашем распоряжении всегда есть готовые шаблоны. А дальше можно, не ограничивая собственную фантазию, изобретать новые способы сбора и анализа данных, чтобы находить уязвимости, как бы хорошо они ни были спрятаны. Надеюсь, моя статья была полезна и поможет вам эффективнее защищать веб-приложения. Не забывайте делиться собственным опытом в комментариях. Увидимся!