Гейты безопасности в CI/CD и threat modeling
Собираем разрозненные сканеры в единый конвейер: настраиваем гейты, которые валят сборку на критичных находках, и учимся думать об угрозах заранее через threat modeling.
Гейт безопасности (security gate) — обязательная автоматическая проверка в пайплайне, которая не пропускает сборку дальше, если найдена уязвимость выше заданного порога серьёзности.
В прошлых уроках мы разобрали отдельные инструменты: SAST, DAST, SCA, сканеры секретов. Но сканер, отчёт которого никто не читает, бесполезен. Сила DevSecOps в том, чтобы сделать эти проверки частью конвейера сборки и связать с проактивным анализом угроз. Этот урок завершает раздел: как превратить находки в гейты и как threat modeling помогает увидеть риски ещё до написания кода.
Зачем это знать команде
Если проверка безопасности — это «отдельный отчёт раз в квартал», её игнорируют под давлением сроков. Если же критичная уязвимость останавливает сборку, как падающий тест, безопасность становится встроенным качеством, а не доброй волей. Это сдвиг культуры: ответственность за безопасность распределяется на всю команду и автоматизируется, а не висит на одном человеке в конце.
Гейт в пайплайне: провал на критичных находках
Технически гейт — это шаг CI, который запускает сканер и возвращает ненулевой код выхода (exit code), если находки превышают порог. Ненулевой код выхода для CI означает «шаг провалился» — и пайплайн останавливается, мердж и деплой блокируются. Концептуально логика гейта такая:
запустить сканер ──> есть находки уровня CRITICAL/HIGH?
да ──> завершиться с ошибкой (exit 1) ──> СБОРКА УПАЛА, мердж заблокирован
нет ──> продолжить пайплайн
В декларативном виде шаг конвейера выглядит примерно так (на примере SCA-сканера, валящего сборку на критичных уязвимостях):
# Шаг CI: гейт безопасности зависимостей
security_scan:
stage: test
script:
# --exit-code 1 = упасть, если найдены уязвимости порога и выше
- trivy fs --severity HIGH,CRITICAL --exit-code 1 .
# упавший шаг блокирует merge request в защищённую ветку
Здесь сборка падёт, если Trivy найдёт зависимость с уязвимостью уровня HIGH или CRITICAL. Аналогично оформляют гейты для SAST (Semgrep с правилом блокировки) и сканера секретов (gitleaks).
Политика порогов: не «ноль находок», а управляемый риск
Слишком строгий гейт («падать на любой находке, включая INFO») быстро бесит команду ложными срабатываниями, и его начинают обходить — это хуже, чем его отсутствие. Поэтому пороги настраивают разумно:
| Уровень находки | Поведение гейта |
| CRITICAL / HIGH | блокировать сборку (fail) — чинить до мерджа |
| MEDIUM | предупреждение, тикет на исправление; не блокировать |
| LOW / INFO | зафиксировать в отчёте для обзора |
Отдельно нужен прозрачный механизм исключений: если находка проверена и признана ложной или неприменимой, её помечают с обоснованием и сроком пересмотра. Так гейт остаётся жёстким, но не превращается в стену, которую все молча сносят.
Где какие гейты включать
Как мы выяснили в первом уроке, инструменты включаются на разных стадиях. На pull request — быстрые SAST и сканер секретов (мгновенная обратная связь автору). На каждый build — SCA по зависимостям. После деплоя на staging — DAST против стенда. Гейт на критичных находках стоит там, где проверка достаточно надёжна, чтобы её провал означал реальную проблему.
Threat modeling: думать об угрозах заранее
Сканеры ищут известные шаблоны в уже написанном коде. Но многие уязвимости — это проблемы дизайна: отсутствующая проверка прав, доверие к внешнему сервису, незащищённый канал. Их не найдёт ни один SAST, потому что код «технически корректен». Threat modeling (моделирование угроз) — это структурированный разбор будущей системы с вопросом «что может пойти не так?» до того, как код написан.
Четыре вопроса threat modeling
Практика опирается на четыре простых вопроса (формулировка из подхода, развиваемого сообществом OWASP):
- Над чем мы работаем? — схема системы: компоненты, потоки данных, границы доверия (где данные переходят из доверенной зоны в недоверенную).
- Что может пойти не так? — перебор угроз для каждого элемента и потока.
- Что мы с этим делаем? — меры защиты для значимых угроз.
- Хорошо ли мы справились? — проверка, что меры адекватны рискам.
Для систематизации угроз удобна мнемоника STRIDE — шесть категорий, по которым проходят каждый компонент:
| Буква | Угроза | Нарушает |
| S — Spoofing | выдача себя за другого | аутентификацию |
| T — Tampering | подмена данных | целостность |
| R — Repudiation | отрицание действия | неотказуемость |
| I — Information disclosure | утечка данных | конфиденциальность |
| D — Denial of Service | отказ в обслуживании | доступность |
| E — Elevation of privilege | повышение прав | авторизацию |
Проходя по схеме системы с этими шестью линзами, команда заранее находит дизайн-риски: «здесь пользователь может выдать себя за другого (S) — нужна аутентификация», «этот поток можно подменить (T) — нужна подпись». Дёшево исправить это на доске, дорого — после релиза.
Как защититься: связать всё вместе
1. Делайте критичные проверки гейтами. SAST, SCA и сканер секретов должны валить сборку на CRITICAL/HIGH, иначе их игнорируют.
2. Настройте разумные пороги и исключения. Гейт без управления порогами либо бесполезен, либо саботируется.
3. Проводите threat modeling на этапе дизайна. Сканеры ловят реализацию, threat modeling ловит архитектурные риски, которые сканеру не видны.
4. Относитесь к безопасности как к качеству. DevSecOps — это не отдельный этап в конце, а свойство всего конвейера: shift-left, автоматизация, общая ответственность команды.
Итоги
- Гейт безопасности — шаг CI, который валит сборку (ненулевой exit code) на находках выше порога; так проверки нельзя проигнорировать.
- Пороги настраивают разумно: блокировать на CRITICAL/HIGH, предупреждать на MEDIUM, с прозрачным механизмом исключений.
- Гейты включают по стадиям: SAST и сканер секретов на PR, SCA на build, DAST на staging.
- Threat modeling отвечает на «что может пойти не так?» на этапе дизайна и ловит архитектурные риски, невидимые сканерам; STRIDE систематизирует категории угроз.
- DevSecOps — это безопасность как встроенное качество всего конвейера, а не финальная галочка.