Привет, Хабр!

С вами Анастасия Березовская, инженер по безопасности процессов разработки приложений в Swordfish Security. В этой статье мы разберемся, как пройти авторизацию в DAST-сканере с помощью прокси. Почему мы решили взяться за эту тему? Сейчас расскажем.

Предыстория

Представьте: на дворе 2024 год, вы работаете AppSec-специалистом в российской компании и решаете приобрести готовый сканер DAST, который запускается одной кнопкой. Идет импортозамещение в IT-отрасли, отечественные вендоры создали несколько решений на замену зарубежным. Самые популярные – Solar appScreener, Positive Technologies Black Box и SolidWall DAST. Все они включают опцию прохождения аутентификации по форме на странице.

И тут возникает проблема: у вас SSO или другая user-friendly многостраничная авторизация. А в настройках страница обязательно должна содержать два поля для ввода логина и пароля и одну кнопку. Конечно, можно просто подставить валидные cookie или токен, но тогда готовое решение каждый раз будет просить вас повторно пройти аутентификацию и обновить необходимую информацию для доступа. Согласитесь, выглядит как отличная задача для стажеров вашего отдела.

“А что, если стажеров мало? И мы вообще-то платим деньги за готовый сканер!” – подумаете вы и решите написать поставщику. Диалог получится примерно такой:

— Здравствуйте! У нас есть двухэтапная авторизация, при которой для каждого поля рендерится отдельная страница в браузере. Сделайте так, чтобы вторым фактором была дата рождения моей бабушки.

— Добрый день! Пожалуйста, заполните заявку на добавление функции в нашем публичном roadmap, и, если она соберет 300 000 уникальных подписей, мы возьмем ее в работу к 2125 году.

TL;DR: рассказываем, как решить данную проблему.

Локально:

  1. Устанавливаем Playwright и запускаем Codegen с запоминанием скрипта на TypeScript и локального хранилища браузера;

  2. Проходим аутентификацию в веб-сервисе;

  3. Получаем файл JSON с данными хранилища и пишем небольшую утилиту на любимом языке программирования, чтобы из файла распарсить значения заголовков или cookie для аутентификации.

На прокси-сервере запускаем:

  1. Скрипт, сгенерированный инструментом Codegen;

  2. Парсер JSON-файла;

  3. Сервер nginx, настроенный как прокси на проверяемый веб-сервис с прокидыванием полученных заголовков.

Запускаем сканирование на адрес прокси-сервера.

Profit! Теперь вы можете поручить стажеру более существенные задачи.

Мотивация

Примерно такой вымышленный сценарий послужил мотивацией для создания доработки готового проекта. Давайте подробнее поговорим о том, как решить проблему аутентификации. Примеры взяты из опыта работы с российскими DAST-сканерами Solar appScreener и PT Black Box.

Настройки аутентификации в обеих программах стандартны: токен, cookie, ключ в запросе, логин/пароль для HTTP Basic и автоматическая аутентификация по форме. Мы условно разделяем их на статические и динамические. Первые настройки используют подготовленные заранее данные доступа (токен, cookie и ключ), вторые подразумевают, что сканер получит эту же информацию, сыграв роль пользователя в браузере.

Проблемы начинаются со свойства времени жизни. Это не касается аутентификации по API-ключу и HTTP Basic. Сложности возникают с токенами и cookie при автоматическом запуске сканера в CI/CD или по расписанию. Недостатки аутентификации по форме видны из настроек: логин и пароль должны располагаться на одной странице с действием "Нажать на кнопку". И не шагом больше (или меньше).

Установить многофакторную аутентификацию для сканирования тоже не получится. Отметим, что в решении от Positive Technologies есть опция "Последовательность авторизаций", но она ограничена набором предыдущих вариантов получения доступа.

Рисунок 1(а). Проблемный кейс: авторизация в Яндекс
Рисунок 1(а). Проблемный кейс: авторизация в Яндекс
Рисунок 1(б). Проблемный кейс: авторизация в Яндекс
Рисунок 1(б). Проблемный кейс: авторизация в Яндекс
Рисунок 2(а). Профиль авторизации в сканере PT BlackBox
Рисунок 2(а). Профиль авторизации в сканере PT BlackBox
Рисунок 2(б). Профиль авторизации в сканере PT BlackBox
Рисунок 2(б). Профиль авторизации в сканере PT BlackBox
Рисунок 3. Профиль авторизации в сканере Solar appScreener
Рисунок 3. Профиль авторизации в сканере Solar appScreener

Вдохновение

Концепция решения проблемы авторизации заключается в установке прокси между DAST-инструментом и сканируемым сервисом. В этом случае аутентификация происходит перед началом сканирования. Однако нам нужно создать способ “универсальной” авторизации, который будет работать и в тех случаях, где стандартные настройки доступа сканеров неприменимы.

Рисунок 4. Концепция прокси для авторизации
Рисунок 4. Концепция прокси для авторизации

В ходе исследования мы обратили внимание на сканеры NetSparker (Invicti) и OWASP ZAP. В интерфейсе первого есть опция "Записать действия пользователя". Для этого в веб-браузере нужно выполнить те же самые шаги, которые клиент обычно совершает для аутентификации. Netsparker автоматически отслеживает и записывает ввод логина и пароля, нажатие клавиш и переход по страницам. При сканировании веб-приложения он использует сохраненные действия клиента для авторизации в системе.

Похожий способ есть и в Open Source-сканере OWASP ZAP. Во встроенном браузере мы также выполняем аутентификацию. По ее завершении записанные действия сохраняются в виде Zest-скрипта. OWASP ZAP повторяет все шаги и выполняет аутентификацию автоматически.

Рисунок 5. "А как у них?": пример работы с авторизацией в сканере NetSparker
Рисунок 5. "А как у них?": пример работы с авторизацией в сканере NetSparker

Поиск

Не углубляясь в дискуссию о границе между плагиатом и вдохновением, мы решили применить аналогичный подход. Подобный метод записи действий пользователя встречается в области тестирования веб-приложений. Легким движением руки По запросу в Google нашелся плагин Selenium IDE. Но его форма расширения для браузера и интерфейс показались нам неудобными, а дизайн сайта и вовсе наводил тоску.

Давний опыт Fullstack-разработки автора напомнил о фреймворке Playwright от Microsoft. Его преимущества не ограничиваются удобным сайтом и хорошей документацией. Фреймворк работает на более низком уровне, чем Selenium IDE, что позволяет обходить многие ограничения, связанные с внутренней архитектурой и реализацией браузеров. Он кажется более производительным за счет асинхронного выполнения действий. В нашей задаче это не основной, но приятный бонус.

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

Рисунок 6. Функциональная схема работы прокси авторизации (бета-версия)
Рисунок 6. Функциональная схема работы прокси авторизации (бета-версия)

Настройка

При запуске Codegen есть опция сохранения конечного состояния браузера и его хранилища в JSON-формате. Их обрабатывает утилита jq, извлекая нужную для аутентификации информацию из хранилища и файлов cookie. После отработки скрипта эти данные автоматически вставляются в конфигурационный файл прокси-сервера.

Модуль универсальной аутентификации разделяется на две части: пользовательскую и серверную. Первая отвечает за запуск Codegen, а вторая – за запуск сгенерированного скрипта и преобразование сохраненных данных доступа в конфигурационный файл прокси-сервера.

Как упоминалось выше, для удобства запуска Codegen все компоненты обернуты в контейнер. Если вы еще не работали с графикой в контейнерах (как и автор до этой задачи), то могут возникнуть сложности. Чтобы запустить Playwright Inspector и браузер из контейнера, можно применить хостовую графическую систему, например монтировать в контейнер файлы X Server-а ОС.

В процессе тестирования мы столкнулись с непредвиденной ситуацией: простой перезапуск сгенерированного скрипта не всегда приводил к ожидаемому результату. Например, страница не успевала прогрузиться, из-за этого веб-хранилище браузера заполнялось не до конца – нужных токенов просто не оказывалось в сохраненных файлах. Нам приходилось вручную добавлять методы ожидания загрузки страницы или элементов в скрипт. В некоторых случаях значения токена или других учетных данных находились в сессионном хранилище, которое Playwright не умеет сохранять – тогда мы сами дописывали несколько строк кода.

Чтобы решить эту проблему, мы разделили процесс создания скрипта на несколько этапов. В начале запустили генератор, но с сохранением тестового файла. Далее исполняется только что созданный и предварительно подготовленный тест, который ожидает полной загрузки страницы, записывает URL успешного входа и сессионное хранилище браузера в файл. При компоновке этих тестов создается конечный скрипт, который будет запускаться на сервере. Этот шаг может показаться излишним, но по-другому все равно пришлось бы "вставлять" адрес успешной авторизации. Кроме того, целостный скрипт для запуска более понятный и удобный для инженера, так как позволяет добавлять в него дополнительные сигнальные строки или элементы успешной аутентификации (как это часто представлено в оригинальных настройках авторизации DAST-сканеров).

Улучшения

Так как автор является инженером по информационной безопасности, мы не можем оставить запуск произвольного кода на сервере без контроля. Поэтому в реализацию добавляем этап с собственными правилами для ESLint. Он написан по принципу "запрещаем всё, что не разрешено". Допускается только:

  • Импортирование пакетов Playwright и fs;

  • Вызов функции require;

  • Ограниченный набор вызываемых методов для объекта browser;

  • Вызов методов для объектов browser и page.

Конечно, это не защищает приложение от всех возможных действий злоумышленников, но точно усложняет им задачу. Да и DAST с нашим прокси запускаются в тестовом окружении.

Отправлять файлы на сервер через SCP — моветон, добавили в реализацию часть выгрузки скрипта из файлового хранилища MinIO. Этот способ может быть заменен на другой.

Рисунок 7. Функциональная схема работы прокси авторизации
Рисунок 7. Функциональная схема работы прокси авторизации

Тестирование

Мы протестировали авторизацию через прокси на приложении Testsparker. В нем есть одна XSS-инъекция, которая видна только залогиненному пользователю. И да, метод с прокси действительно работает. Пруфы, не фотошоп :)

Рисунок 8(а). Результат работы сканера на оригинальном приложении без профиля авторизации
Рисунок 8(а). Результат работы сканера на оригинальном приложении без профиля авторизации
Рисунок 8(б). Результат работы сканера через прокси
Рисунок 8(б). Результат работы сканера через прокси

Результат

Разработанное прокси значительно облегчает прохождение аутентификации в DAST-сервисах. Достаточно один раз в полуавтоматическом режиме создать скрипт, который в дальнейшем автоматически будет повторять необходимые действия и подставлять параметры авторизации к каждому отправляемому запросу на всех сканированиях без участия человека.

Есть кейсы, когда и такие костыли не помогают. В SPA-приложениях авторизация пользователя часто определяется по наличию параметров в хранилище – их добавление в запросы ничего не дает. Один из таких примеров – главная страница приложения Juice Shop.

Незначительный минус: для сканера все пути теперь доступны с аутентификацией. Поэтому обнаружить, какие ручки не прикрыты, можно только двумя запусками.

Автор: Анастасия Березовская

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