OWASP ZAP: автоматический скан своего веб-приложения перед релизом

0
10
Обложка статьи: OWASP ZAP: автоматический скан своего веб-приложения перед релизом

Статическим анализом и сканированием образов (bandit, Trivy) не находятся ошибки, которые проявляются только в работающем приложении: отсутствующие заголовки безопасности, проблемы с cookie, утечка версий через ответы сервера. OWASP ZAP проверяет именно работающее приложение — своё собственное, на тестовом или стейджинг-окружении, никогда без разрешения владельца системы.

Статья опирается на логику направления «Тесты на проникновение» из методики зрелости Группы Сбер (источник); это методика для крупных компаний, мы используем её как ориентир и пересказываем своими словами. Проверка выполнена скриптом zap-baseline.py из официального образа ZAP против тестового контейнера nginx, после неё всё удалено.

Что должно получиться

Уровень Что это значит на практике
2. Гигиенический Веб-приложение проверяется автоматическим сканером на типовые проблемы перед каждым релизом, а не только при внешнем пентесте раз в год

Критерии сформулированы нами по мотивам методики, это ориентир, а не оценка соответствия.

Что такое baseline-скан

У ZAP два принципиально разных режима: активное сканирование (пытается подставлять в параметры тестовые значения, может создать нагрузку и побочные эффекты — только по полноценному согласованию, как в обычном пентесте) и пассивное, которым и является zap-baseline.py: скрипт обходит приложение как обычный паук и анализирует только то, что приложение само отдаёт в ответах, без попыток что-то взломать. Это делает его безопасным для регулярного запуска в конвейере сборки против собственного стейджинга.

Запуск

docker run --rm -t ghcr.io/zaproxy/zaproxy:stable zap-baseline.py \
  -t https://staging.example.com/ -r baseline-report.html

Ключ -r сохраняет полный HTML-отчёт. При монтировании каталога для отчёта убедитесь, что он доступен для записи процессу контейнера (ZAP работает не от root) — иначе получите AccessDeniedException при попытке сохранить файл.

Результат проверки

На тестовом сервере nginx с настройками по умолчанию скан прошёл 59 проверок с результатом PASS и дал 8 предупреждений:

Находка Что это значит
Missing Anti-clickjacking Header Нет X-Frame-Options или эквивалента в CSP — страницу можно встроить во фрейм на чужом сайте
X-Content-Type-Options Header Missing Браузер может попытаться угадать тип содержимого вместо того, чтобы доверять заголовку сервера
Server Leaks Version Information via “Server” Header Версия веб-сервера видна в ответе — упрощает подбор эксплойта под конкретную версию
Content Security Policy (CSP) Header Not Set Нет политики, ограничивающей источники скриптов и ресурсов на странице
Permissions Policy Header Not Set Нет ограничения на доступ страницы к камере, геолокации и другим API браузера

Все находки — типовые отсутствующие заголовки безопасности, то же самое, что можно добавить правилами из статьи про заголовки безопасности в nginx. Критичных находок (SQL-инъекция, XSS, раскрытие исходного кода) на тестовом сервере не нашлось — ожидаемо для статической страницы без динамики.

Коды возврата для конвейера

По умолчанию скрипт возвращает ненулевой код, если есть находки уровня предупреждения (WARN-NEW) или выше — в тесте без дополнительных флагов результат вернул код 2. Ключ -I меняет это поведение: предупреждения не влияют на код возврата, завершение кодом 0 происходит, даже если предупреждений много.

zap-baseline.py -t https://staging.example.com/        # код 2 при предупреждениях
zap-baseline.py -t https://staging.example.com/ -I      # предупреждения не блокируют сборку

Чтобы остановить сборку только на конкретных, действительно критичных правилах, сгенерируйте файл конфигурации и переведите нужные строки с WARN на FAIL:

zap-baseline.py -t https://staging.example.com/ -g rules.conf   # создаёт шаблон со всеми правилами как WARN
# в rules.conf: смените WARN на FAIL для нужных идентификаторов правил, например:
#   10020	FAIL	(Anti-clickjacking Header)
zap-baseline.py -t https://staging.example.com/ -c rules.conf

В тесте перевод правила 10020 (отсутствие заголовка защиты от кликджекинга) в FAIL дал в выводе строку FAIL-NEW именно по этому правилу и код возврата 1, остальные находки остались предупреждениями и на код возврата не повлияли.

Порядок внедрения

  1. Запустите без флагов на стейджинге и получите исходный список находок — это ваша точка отсчёта, а не повод останавливать все релизы сразу.
  2. Создайте файл конфигурации правил и переведите в FAIL только то, что реально нужно блокировать (обычно: явные уязвимости, не стилистические предупочтения).
  3. Встройте в конвейер после деплоя на стейджинг, перед деплоем на продуктив.
  4. Для динамических приложений с формами и API дополните ключами аутентификации (-U для входа под тестовым пользователем) — без входа паук увидит только публичные страницы.
  5. Активное сканирование — отдельная история: проводите его как полноценный пентест, с согласованием окна работ и уведомлением дежурных: подготовка к пентесту.

Диагностика

Симптом Причина и что делать
AccessDeniedException при сохранении отчёта Смонтированный каталог недоступен для записи процессу контейнера, который работает не от root. Дайте каталогу права на запись для непривилегированного пользователя
Сборка падает на каждом релизе из-за предупреждений Используется код возврата по умолчанию без разбора находок. Либо добейте находки, либо используйте файл конфигурации правил для разумного порога
Паук видит только главную страницу Приложение требует входа или сильно построено на JavaScript. Настройте контекст аутентификации или добавьте ключ для современного паука (-j)
Активный скан нарушил работу стейджинга Активное сканирование — не пассивный baseline. Проводите его только в согласованное окно, отдельно от регулярного пассивного скана

Ограничения

Baseline-скан — это пассивный анализ ответов приложения, он не находит уязвимости, которые требуют активного взаимодействия (слепую SQL-инъекцию, логические ошибки авторизации). Для такой глубины нужен либо активный скан в согласованном окне, либо полноценный тест на проникновение с участием человека.

Где это в модели зрелости

Материал относится к направлению «Тесты на проникновение» (материалы направления, карта зрелости). Уровень 2 требует регулярной автоматической проверки веб-приложений, а не только разовых внешних тестов: пассивный скан в конвейере закрывает типовые проблемы до того, как до них дойдёт очередь у внешнего пентестера.

Как проверить

  1. Скан запускается автоматически на стейджинге перед каждым релизом в продуктив.
  2. Порог для остановки сборки задан осознанно через файл конфигурации правил, а не оставлен «как получится».
  3. Находки уровня предупреждения разобраны и исправлены или приняты с обоснованием.
  4. Активное сканирование и пентест проводятся отдельно, с согласованием, не как часть ежедневного пассивного скана.

Источники

  • Методика оценки уровня зрелости кибербезопасности компании, Группа Сбер: sberbank.ru.
  • Документация OWASP ZAP, baseline-скан: zaproxy.org; OWASP Top 10: owasp.org.

Комментарии закрыты.