На одном проекте мы полгода гоняли SAST в пайплайне. Зелёные галочки, чистый код, всё красиво. Потом пен-тестеры за час нашли SQL-инъекцию в API. Статический анализатор просто не знал, что этот эндпоинт принимает пользовательский ввод в неочевидном формате.
Вот так я понял: одного статического анализа мало. Нужен DAST.
SAST, DAST, SCA — собери коллекцию
Разберёмся в алфававите безопасности.
SAST (Static Application Security Testing) — статический анализ. Смотрит исходный код, не запуская его. Находит небезопасные функции, хардкод секретов, уязвимые зависимости. Работает быстро, но даёт много ложных срабатываний.
DAST (Dynamic Application Security Testing) — динамический анализ. Атакует работающее приложение извне. Эмулирует хакера: сканирует эндпоинты, пробует инъекции, ищет открытые директории. Не видит код, но находит реальные уязвимости в рантайме.
SCA (Software Composition Analysis) — анализ зависимостей. Проверяет библиотеки на известные CVE. Npm audit, safety, Dependabot — это всё SCA.
По-хорошему, нужно всё три. Они не дублируют, а дополняют друг друга. SAST найдёт eval() в коде, SCA скажет, что lodash 4.17.15 уязвим, а DAST обнаружит, что админка доступна без авторизации.
Почему SAST недостаточен
Статика крутая. Я сам использую её в каждом проекте. Но у неё есть фундаментальные ограничения.
SAST не понимает контекст. Он видит функцию getUser(id) и не знает, откуда приходит id — из доверенного конфига или из HTTP-параметра. Поэтому либо пропускает уязвимости, либо орет на каждый SELECT * FROM.
DAST работает с тем, что реально исполняется. Ему плевать на исходники. Он стучится в работающее приложение и смотрит: а что если в поле username отправить ' OR '1'='1? Если сервер ответил ошибкой или отдал все записи — уязвимость реальна.
Есть ещё одна проблема. Сборка. В проде работает не тот код, что в репозитории. Есть Docker-слои, переменные окружения, конфиги nginx. SAST этого не видит. DAST — видит, потому что атакует именно то, что развёрнуто.
Но и DAST не панацея. Он не найдёт хардкоженный API-ключ в коде. Не скажет, что ты используем устаревшую версию React. И стоит дорого по времени — полноценное сканирование может идти часами.
DAST в деталях: как это работает
DAST-сканер — это робот-пентестер. Он работает по такому алгоритму:
Сначала краулер обходит приложение. Находит все страницы, формы, API-эндпоинты. Потом фаззер отправляет вредоносные payloads в каждое поле. SQL-инъекции, XSS, path traversal, command injection — сотни вариантов.
Анализатор смотрит на ответы. Ошибки базы данных? Аномальное время ответа? Редирект на сторонний ресурс? Это признаки уязвимости.
# Пример простого DAST-запроса
curl -X POST "https://api.example.com/users" \
-H "Content-Type: application/json" \
-d '{"email": "test@example.com; DROP TABLE users;--"}'
Хороший DAST умеет аутентифицироваться. Залогинился, получил сессию, пошёл сканировать защищённые области. Без этого проверяется только публичная часть — половина атаковой поверхности остаётся за бортом.
Инструменты: что выбрать
OWASP ZAP — классика. Open source, бесплатный, интегрируется во всё. UI убогий, но для CI/CD норм. Есть Docker-образ, API, куча плагинов.
Burp Suite Professional — индустриальный стандарт. Стоит денег, но пен-тестеры его обожают. Автоматическое сканирование крутое, ручной тестинг — ещё лучше.
Nucleus, Invicti, Acunetix — коммерческие решения. Дороже, но с красивыми отчётами для менеджмента и меньше ложных срабатываний.
GitLab DAST — встроен в GitLab CI. Если вы на GitLab, это самый простой путь. Конфигурируется одной строкой.
# .gitlab-ci.yml
stages:
- test
dast:
stage: test
image: registry.gitlab.com/gitlab-org/security-products/dast:latest
variables:
DAST_WEBSITE: https://staging.example.com
DAST_AUTH_URL: https://staging.example.com/login
DAST_USERNAME: "testuser"
DAST_PASSWORD: "testpass"
script:
- /analyze
artifacts:
reports:
dast: gl-dast-report.json
Честно? Для начала хватит OWASP ZAP. Бесплатно, функционал достаточный. Когда упрётесь в ограничения — думайте о платных решениях.
Внедрение в CI/CD
Вот тут начинаются сложности. DAST требует работающего приложения. Статику можно гнать на коммите. DAST нужно развёрнутое окружение.
Обычно сканируют staging или review-окружения. Подняли ветку в изолированном контейнере — прогнали DAST — отдали отчёт.
# Пример для GitHub Actions с OWASP ZAP
name: DAST Scan
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
dast:
runs-on: ubuntu-latest
steps:
- name: Deploy to staging
run: |
docker-compose -f docker-compose.staging.yml up -d
sleep 30 # Ждём поднятия сервиса
- name: ZAP Scan
uses: zaproxy/action-full-scan@v0.7.0
with:
target: 'http://localhost:8080'
cmd_options: '-a'
- name: Tear down staging
if: always()
run: docker-compose -f docker-compose.staging.yml down
Важно не блокировать деплой на каждый finding. DAST даёт ложные срабатывания. На одном проекте мы сначала падали на любой alert, потом устали разруливать false positives, и сделали так: критические уязвимости блокируют, остальные просто логируются в MR.
# Скрипт для парсинга отчёта ZAP
import json
def parse_zap_report(report_path):
with open(report_path) as f:
report = json.load(f)
critical = []
for site in report.get('site', []):
for alert in site.get('alerts', []):
if alert['riskcode'] == '3': # High risk
critical.append({
'name': alert['name'],
'url': alert['instances'][0]['uri'],
'solution': alert['solution']
})
return critical
# Блокируем пайплайн только при критических уязвимостях
critical_vulns = parse_zap_report('zap-report.json')
if critical_vulns:
print(f"Found {len(critical_vulns)} critical vulnerabilities!")
exit(1)
Как мы это делали в стартапе
Небольшая история. Финтех-стартап, микросервисы на Python, деплой в Kubernetes. Внедряли безопасность поэтапно.
Первым делом добавили SCA. pip-audit для Python, npm audit для фронтенда. Это дало низко висящие плоды — нашли 23 уязвимости в зависимостях, за час закрыли критические.
Потом SAST. Semgrep — лёгкий, быстрый, понятные правила. Настроили за вечер. Первые две недели разбирали ложные срабатывания, потом привыкли.
DAST стал третьим этапом. OWASP ZAP в GitLab CI, сканирование staging перед каждым релизом. Нашёл открытый actuator-эндпоинт в Spring Boot и утечку метаданных в API. Исправили.
Полный цикл: коммит → SAST + SCA → билд → деплой на staging → DAST → прод. Да, дольше. Да, иногда бесит. Но спать спокойнее.
Сравнение подходов: табличка
Если коротко:
| Подход | Что находит | Скорость | False positives | Требует запуска |
|---|---|---|---|---|
| SAST | Уязвимости в коде | Минуты | Много | Нет |
| SCA | CVE в зависимостях | Секунды | Мало | Нет |
| DAST | Уязвимости в рантайме | Часы | Средне | Да |
Идеальная комбинация: SCA на каждый коммит, SAST на MR, DAST на релиз-кандидат. Так вы ловите проблемы максимально рано, но не тормозите разработку.
Что по автоматизации
Ручной запуск DAST — это костыль. Забудете. Нужно в CI/CD.
Но есть нюанс. Полное сканирование идёт 2-4 часа. На каждый MR — непозволительная роскошь. Решение: инкрементальное сканирование или baseline-подход.
При baseline вы впервые сканируете приложение полностью, сохраняете результаты. Потом при каждом запуске сравниваете: появились ли новые уязвимости? Если нет — зелёный свет. Если да — разбираться.
# ZAP baseline scan — быстро, но поверхностно
docker run -t owasp/zap2docker-stable zap-baseline.py \
-t https://staging.example.com \
-c baseline.conf \
-r baseline-report.html
# Full scan — долго, но глубоко
docker run -t owasp/zap2docker-stable zap-full-scan.py \
-t https://staging.example.com \
-c full-scan.conf \
-r full-report.html
Ещё вариант — анализ кода max, когда вы комбинируете все три подхода в едином пайплайне. Перебор? Возможно. Но для fintech, medtech и других регулируемых областей — необходимость.
DAST — не замена статическому анализу, а дополнение. Если вы сейчас используете только SAST — это уже хорошо. Но если обрабатываете персональные данные или деньги, добавьте DAST. Хотя бы на staging.
Кстати, если нет времени настраивать зоопарк сканеров, посмотрите на Distiq. Это AI-бот для code review, который находит уязвимости, баги и проблемы производительности прямо в MR. Интегрируется за пару минут, поддерживает основные языки. Не заменяет DAST, но закрывает большую часть вопросов по качеству кода до того, как он попадёт в рантайм.
