Последние два года наша команда прикручивает security-пайплайны продуктовым командам - и в семи случаях из десяти застаём одну картину: кто-то добавил Bandit с дефолтным конфигом, получил 400+ алертов на первом прогоне и через неделю поставил
allow_failure: true на все security-джобы. Формально SAST в pipeline есть, стейдж нарисован. По факту - декорация. С DAST-сканированием ещё веселее: полный прогон ZAP занимает 40 минут, и его просто отключают, потому что он ломает окно релиза. Автоматизация безопасности в pipeline живёт на слайдах, а реальные уязвимости находят пентестеры раз в год - или атакующие в продакшене.Эта статья - про то, как выстроить внедрение SAST DAST в CI/CD так, чтобы оно ловило критичные находки, не генерировало шум и не тормозило доставку кода. Подход - из практики DevSecOps-инженера с пентестерским бэкграундом: я понимаю, что ищут атакующие, и умею переводить это в правила анализа.
CI/CD-пайплайн как поверхность атаки: зачем это знать пентестеру
Прежде чем встраивать сканеры, стоит посмотреть на pipeline глазами атакующего. CI/CD - полноценная поверхность атаки с собственными TTPs в MITRE ATT&CK. Подробнее - в нашем подробном разборе атаки на цепочку поставок.Poisoned Pipeline Execution (T1677, Execution) - атакующий модифицирует CI-конфигурацию (.gitlab-ci.yml, Jenkinsfile, GitHub Actions workflow) для выполнения произвольного кода в контексте runner'а. Если runner имеет доступ к production-секретам - это прямой путь к компрометации инфраструктуры. Любой разработчик с правом на merge request потенциально может изменить pipeline. Звучит безобидно, пока не вспомнишь, что runner'ы часто крутятся с теми же кредами, что и деплой.
Compromise Software Dependencies and Development Tools (T1195.001, Initial Access) - supply chain через вредоносную зависимость. Typosquatting в PyPI или npm, скомпрометированный мейнтейнер, малварь в транзитивной зависимости третьего уровня. По OWASP это A06:2021 (Vulnerable and Outdated Components). После Log4Shell и XZ Utils SCA-сканирование из «желательного» стало обязательным - тут без вариантов.
Credentials In Files (T1552.001, Credential Access) - API-ключи, токены, пароли, захардкоженные в коде или конфигах. Попадают в Git-историю и остаются доступными даже после удаления из HEAD. Один AWS-ключ в публичном репозитории - и через минуты начинают крутить криптомайнеры. Я видел, как ключ от S3 утекал через
.env, который «случайно» попал в коммит.Exploit Public-Facing Application (T1190, Initial Access) - то, что DAST моделирует: injection-атаки, XSS, SSRF, misconfiguration. По OWASP это A03:2021 (Injection) и сопутствующие риски.
Code Repositories (T1213.003, Collection) - репозиторий как источник данных для атакующего: документация, внутренние API-эндпоинты, схемы баз, комментарии с TODO вроде «убрать хардкод пароля перед релизом». Такие TODO - подарок для разведки.
Shift-left безопасность одновременно уменьшает поверхность атаки приложения и защищает сам pipeline. Для пентестера это значит: если на проекте грамотно настроен DevSecOps-пайплайн, low-hanging fruit в виде типовых SQLi и XSS уже отфильтрован. Ценность ручного тестирования - в бизнес-логике, цепочках уязвимостей и authorization bypass. По данным исследования totalshiftleft.com, уязвимость, которая стоит $500 на этапе написания кода, может обойтись в $50 000+ после попадания в прод - с учётом incident response, экстренного патчинга и уведомления пользователей.
Инструменты статического и динамического анализа: trade-off таблица
Главный критерий выбора SAST-инструментов для разработчиков - не мощность сканера, а соотношение скорости, точности и стоимости поддержки. Слишком медленный сканер отключат. Слишком шумный - проигнорируют. Я это видел десятки раз.| Инструмент | Плюсы | Минусы | Когда использовать |
|---|---|---|---|
| Semgrep (SAST) | Секунды на diff, 30+ языков, кастомные YAML-правила, низкий FP rate | Dataflow-анализ слабее коммерческих решений | MR/PR diff-анализ, мультиязычные проекты |
| Bandit (SAST, Python) | Глубокий Python-анализ (eval, pickle, subprocess) | Только Python, высокий FP без тюнинга pyproject.toml | Python-проекты, pre-commit хуки |
| SonarQube (SAST) | Дашборд техдолга, интеграция с Jira, широкое покрытие | Отдельный сервер, медленнее на полном скане | Enterprise с бюджетом, полный анализ кодовой базы |
| Trivy (SCA/Container) | Быстрый, бесплатный, FS + образы + SBOM | Не анализирует собственный код | Каждый build, контейнерные среды |
| OWASP ZAP (DAST) | Flagship OWASP, baseline + full scan | Full scan 20-60 мин, средний FP rate | Staging, scheduled pipeline |
| Nuclei (DAST) | Template-based, минуты, низкий FP | Не находит zero-day, только известные паттерны | CI + custom templates, quick check |
[Применимо: внутренний DevSecOps, все типы проектов. Для external pentest - Nuclei и ZAP используются напрямую, без CI-обвязки]
Все инструменты в таблице - open-source и активно поддерживаются: Semgrep - Semgrep Inc (ранее r2c), Trivy - Aqua Security, ZAP - OWASP Foundation, Nuclei - ProjectDiscovery, Bandit - PyCQA. Для enterprise-сценариев есть коммерческие альтернативы: Checkmarx SAST, Veracode, PT Application Inspector от Positive Technologies - с расширенной отчётностью и управлением ложными срабатываниями на уровне политик.
Пошаговое внедрение SAST и DAST в pipeline
Требования к окружению
- CI/CD: GitLab CI/CD с Docker-executor (примеры ниже для GitLab; для GitHub Actions логика та же, синтаксис .yml отличается)
- Runner: минимум 2 ГБ RAM, рекомендуется 4 ГБ для параллельных security-джоб
- Docker: для запуска контейнеризованных сканеров (ZAP, Trivy, Semgrep)
- Staging: окружение, доступное из runner'а по HTTP/HTTPS, для DAST-сканирования
- Semgrep CLI v1.x: бесплатный,
pip install semgrepили Docker-образsemgrep/semgrep:latest - OWASP ZAP: Docker-образ
ghcr.io/zaproxy/zaproxy:stable - Trivy v0.50+: Docker-образ
aquasec/trivy:latestили системная установка - Сеть: runner должен иметь доступ к registry.semgrep.dev (для загрузки правил) и к staging-URL
Шаг 1: Pre-commit - перехват секретов до Git-истории
Pre-commit - самый ранний рубеж shift-left безопасности. Gitleaks в pre-commit хуке не даст разработчику закоммитить AWS-ключ, JWT-токен или пароль от базы. Bandit в том же pre-commit поймает опасные Python-конструкции:eval(), pickle.loads(), subprocess.call(shell=True).Настройка через
.pre-commit-config.yaml с кастомной конфигурацией Bandit в pyproject.toml (секция [tool.bandit]) позволяет вырезать false positive для конкретного проекта - например, тесты, которые намеренно используют опасные конструкции. Это прямое противодействие технике Credentials In Files (T1552.001): секреты перехватываются до попадания в Git-историю.Ограничение: pre-commit работает на локальной машине. Если разработчик не установил хуки или пробросил коммит через
--no-verify - находки уйдут в репозиторий. Поэтому дублирование Gitleaks и Bandit в CI/CD-джобах обязательно. Это страховка от человеческого фактора, и без неё вся конструкция рассыпается.Шаг 2: SAST в diff-режиме на каждый merge request
Принцип: сканировать только изменённые файлы, блокировать только Critical/High. Полный скан всей кодовой базы - для ночного scheduled-пайплайна.
YAML:
semgrep-sast:
stage: test
image: semgrep/semgrep:latest
script:
- semgrep ci --config auto --severity ERROR
--json -o semgrep.json
artifacts:
paths: [semgrep.json]
when: always
rules:
- if: $CI_MERGE_REQUEST_IID
--config auto подключает community-правила Semgrep, оптимизированные для security. --severity ERROR - pipeline падает только на Critical/High. Обратите внимание: нет allow_failure: true. Если Semgrep нашёл SQL-инъекцию или hardcoded secret - merge request блокируется до исправления. Джоба привязана к merge request через $CI_MERGE_REQUEST_IID и не запускается на прямые push.Для medium-находок добавьте вторую джобу: тот же Semgrep с
--severity WARNING и allow_failure: true. Результаты отправляйте в Defect Dojo или Jira через API - это backlog технического долга по безопасности, который не блокирует релиз, но отслеживается. Без трекинга medium-находки превращаются в «потом разберёмся», а «потом» не наступает никогда.Параллельно на стейдже
security запускайте SCA-сканирование: trivy image --exit-code 1 --severity CRITICAL,HIGH $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_NAME. Trivy проверяет Docker-образ на известные CVE в системных пакетах и библиотеках - слой, который SAST принципиально не видит. Для проверки репозитория целиком (включая файлы, не попавшие в образ при multi-stage build) добавьте trivy fs --exit-code 1 --severity CRITICAL ./ в отдельную джобу.Шаг 3: DAST-сканирование на staging
DAST запускается после деплоя - ему нужно работающее приложение. Это динамическое тестирование безопасности снаружи, по сути автоматизированный recon.Два режима, два расписания:
Baseline scan (пассивный, 2-5 минут) - на каждый деплой в staging:
YAML:
zap-baseline:
stage: post-deploy
image: ghcr.io/zaproxy/zaproxy:stable
script:
- zap-baseline.py -t $STAGING_URL
-J report.json -l WARN
artifacts:
paths: [report.json]
when: always
rules:
- if: $CI_COMMIT_BRANCH == "main"
Full scan (активный, 20-60 минут) - в scheduled pipeline, ночью или раз в неделю. Замените
zap-baseline.py на zap-full-scan.py. ZAP отправит реальные payloads: SQL-инъекции, XSS-векторы, SSRF-пробы, проверки аутентификации. Перед мажорными релизами запускайте вручную и разбирайте результаты с командой. Это тот случай, когда 40 минут ожидания оправданы.Nuclei как дополнение:
nuclei -u $STAGING_URL -t cves/ -t misconfigurations/ -severity critical,high занимает 1-3 минуты и проверяет staging на известные CVE в используемых технологиях (конкретная версия Nginx, Redis, Elasticsearch). Шаблоны обновляются community ProjectDiscovery - покрытие в тысячи проверок. Nuclei хорошо ловит то, что ZAP baseline пропускает: специфичные CVE для конкретных версий софта. На одном проекте именно Nuclei нашёл открытый Elasticsearch 7.10 с CVE-2021-22145, который ZAP baseline не зацепил.Ограничение DAST в CI/CD: автоматический DAST-скан не заменяет authenticated testing с несколькими ролями. Проверка горизонтальной эскалации (user A видит данные user B) требует настроенных auth-контекстов в ZAP - это отдельная задача конфигурации, выходящая за рамки базового внедрения.
Security gates в pipeline: severity-пороги без паралича
Quality gate - точка, где pipeline проходит или падает. Настройка порогов - самая политически нагруженная часть интеграции безопасности в DevOps, потому что затрагивает скорость работы всей команды. Тут нельзя прийти и сказать «теперь всё блокируем» - получите саботаж.Рабочая стратегия - поэтапное ужесточение. Модель опирается на подход OWASP DSOMM (DevSecOps Maturity Model), который определяет категории Build, Test, Information Gathering и Culture для оценки зрелости DevSecOps-процессов.
Месяц 1-3: Advisory. Все security-джобы с
allow_failure: true. Результаты видны в MR-комментариях, ничего не блокируют. Цель - собрать baseline: среднее число находок на MR, процент false positive, какие правила генерируют шум. Тюните ruleset: добавляйте исключения, кастомизируйте Semgrep-правила под кодовую базу. Если после трёх месяцев false positive rate выше 30% - не переходите к блокировке, продолжайте тюнинг. Серьёзно, не торопитесь. Преждевременная блокировка убьёт доверие команды к инструментам.Месяц 4-6: Blocking Critical/High.
allow_failure: false на SAST- и SCA-джобах для severity ERROR. Medium - в advisory. Команда привыкает к блокировкам и учится исправлять находки без эскалации. Целевой false positive rate - ниже 20%.Месяц 7+: Полные гейты. Critical/High блокируют merge. Medium создаёт тикет в Jira/Defect Dojo с SLA 30 дней. DAST baseline - обязательный шаг перед production deploy. Метрики для отслеживания: MTTR (среднее время до исправления), escape rate (уязвимости, дошедшие до прода), false positive rate.
Ключевое правило: каждый алерт должен содержать конкретную рекомендацию. Semgrep позволяет добавлять
fix: и message: в кастомные правила - разработчик видит не абстрактное «потенциальная SQL-инъекция», а конкретный параметризованный запрос для замены. Инструмент, генерирующий сотни необъяснимых предупреждений, будет отключён командой в первую неделю. И правильно - такой инструмент безопасность не повышает, а создаёт иллюзию процесса.Что пропустят SAST и DAST: ограничения автоматики
📚 Часть контента скрыта. Этот материал доступен участникам сообщества с рангом One Level или выше
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Получить доступ просто — достаточно зарегистрироваться и проявить активность на форуме
Вывод для пентестера: если на проекте работает зрелый DevSecOps-пайплайн, не тратьте первые два дня на поиск типовых SQLi и hardcoded credentials. Фокусируйтесь на бизнес-логике, авторизации, цепочках - там реальная ценность ручного тестирования. Автоматика - фильтр грубой очистки, не замена человеческому мышлению.
Я видел десятки проектов, где команда ставила Semgrep + ZAP + Trivy и считала безопасность закрытой. А потом на пентесте находился IDOR в API переводов, который позволял списывать деньги с чужих счетов - через абсолютно валидные HTTP-запросы, которые ни один сканер не пометил. Инструменты закрыли injection и outdated components, но бизнес-логика осталась без присмотра. Большинство команд застревают именно здесь: SAST/DAST стоят, алерты летят, но никто не тюнит правила, не мерит false positive rate и не строит процесс вокруг находок. Security-стейдж превращается в строчку пайплайна, а не в работающий контроль. Относитесь к security gates как к продукту: итерируйте правила, настраивайте пороги, замеряйте escape rate раз в квартал. Это не разовый проект внедрения - это непрерывный процесс, который нужно кормить вниманием. Если хочется выстроить его системно - от архитектуры пайплайна до управления уязвимостями и метрик зрелости - курс «Профессия DevSecOps-инженер» на Codeby Academy собран теми, кто сам прошёл этот путь от пентеста к автоматизации безопасности.