Гейты безопасности в 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 — это безопасность как встроенное качество всего конвейера, а не финальная галочка.
Проверьте себя
1. Что делает шаг CI «гейтом безопасности»?
AОн возвращает ненулевой код выхода при находках выше порога, из-за чего пайплайн падает и блокирует мердж/деплой
BОн отправляет отчёт сканера на почту команде раз в неделю
CОн шифрует исходный код перед сборкой
DОн просто логирует находки, не влияя на ход сборки
2. Почему threat modeling находит уязвимости, которые пропускает SAST?
AThreat modeling разбирает дизайн системы и архитектурные риски до написания кода, а SAST ищет известные шаблоны в уже написанном, технически корректном коде
BThreat modeling запускает приложение и атакует его снаружи, как DAST
CSAST работает только с зависимостями, а threat modeling — с исходным кодом
DНикакой разницы нет, это одно и то же