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, остальные находки остались предупреждениями и на код возврата не повлияли.
Порядок внедрения
- Запустите без флагов на стейджинге и получите исходный список находок — это ваша точка отсчёта, а не повод останавливать все релизы сразу.
- Создайте файл конфигурации правил и переведите в
FAILтолько то, что реально нужно блокировать (обычно: явные уязвимости, не стилистические предупочтения). - Встройте в конвейер после деплоя на стейджинг, перед деплоем на продуктив.
- Для динамических приложений с формами и API дополните ключами аутентификации (
-Uдля входа под тестовым пользователем) — без входа паук увидит только публичные страницы. - Активное сканирование — отдельная история: проводите его как полноценный пентест, с согласованием окна работ и уведомлением дежурных: подготовка к пентесту.
Диагностика
| Симптом | Причина и что делать |
|---|---|
AccessDeniedException при сохранении отчёта |
Смонтированный каталог недоступен для записи процессу контейнера, который работает не от root. Дайте каталогу права на запись для непривилегированного пользователя |
| Сборка падает на каждом релизе из-за предупреждений | Используется код возврата по умолчанию без разбора находок. Либо добейте находки, либо используйте файл конфигурации правил для разумного порога |
| Паук видит только главную страницу | Приложение требует входа или сильно построено на JavaScript. Настройте контекст аутентификации или добавьте ключ для современного паука (-j) |
| Активный скан нарушил работу стейджинга | Активное сканирование — не пассивный baseline. Проводите его только в согласованное окно, отдельно от регулярного пассивного скана |
Ограничения
Baseline-скан — это пассивный анализ ответов приложения, он не находит уязвимости, которые требуют активного взаимодействия (слепую SQL-инъекцию, логические ошибки авторизации). Для такой глубины нужен либо активный скан в согласованном окне, либо полноценный тест на проникновение с участием человека.
Где это в модели зрелости
Материал относится к направлению «Тесты на проникновение» (материалы направления, карта зрелости). Уровень 2 требует регулярной автоматической проверки веб-приложений, а не только разовых внешних тестов: пассивный скан в конвейере закрывает типовые проблемы до того, как до них дойдёт очередь у внешнего пентестера.
Как проверить
- Скан запускается автоматически на стейджинге перед каждым релизом в продуктив.
- Порог для остановки сборки задан осознанно через файл конфигурации правил, а не оставлен «как получится».
- Находки уровня предупреждения разобраны и исправлены или приняты с обоснованием.
- Активное сканирование и пентест проводятся отдельно, с согласованием, не как часть ежедневного пассивного скана.
Источники
- Методика оценки уровня зрелости кибербезопасности компании, Группа Сбер: sberbank.ru.
- Документация OWASP ZAP, baseline-скан: zaproxy.org; OWASP Top 10: owasp.org.


















