Модель угроз и базовая гигиена инфраструктуры
Урок курса «Курс подготовки DevOps-инженеров»
Хронометраж: занятие 60 минут — теория 30 минут, практика 30 минут. Домашняя работа: 1–2 часа.
Кратко о чём урок: Что защищаем и от кого, поверхность атаки типового сервиса, разбор реалистичных сценариев (утёкший ключ CI, открытый порт БД, уязвимая зависимость), приоритизация мер по риску.
1. Что изучаем
- строить модель угроз по четырём вопросам: что строим, что может пойти не так, что сделаем, достаточно ли;
- проходить STRIDE по элементам системы и находить классы угроз, а не только очевидные;
- перечислять фактическую поверхность атаки командами и сверять её со схемой;
- отличать находку от секрета (триаж) и приоритизировать по риску;
- применять базовую гигиену — минимум, отсекающий дешёвые атаки, и понимать DevSecOps.
2. Зачем это нужно и где применяется
Безопасность не прикручивают в конце — её проектируют. Модель угроз — это структурированный способ заранее спросить «что может пойти не так» и решить, что с этим делать, пока система на схеме, а не в проде под атакой. Базовая гигиена — дешёвый минимум (наименьшие привилегии, нет секретов в коде, минимум открытых портов, обновления), который отсекает большинство массовых атак; без неё дорогие средства защиты бессмысленны.
Это первый урок блока 18, и он задаёт рамку для остальных: управление доступом (18.02), секреты (18.03), цепочка поставок (18.04), сканирование (18.05), харденинг (18.06), политики Kubernetes (18.07), сеть (18.08), реакция на CVE (18.09), аудит (18.10) — всё это конкретные ответы на угрозы, которые модель угроз помогает увидеть. DevSecOps — это встраивание этих проверок в конвейер (блок 09), чтобы безопасность была общей ответственностью и автоматической, а не разовым аудитом раз в год.
3. Теория (30 минут)
Модель угроз отвечает на четыре вопроса. Определение OWASP: «Threat modeling works to identify, communicate, and understand threats and mitigations within the context of protecting something of value.» Процесс организован четырьмя вопросами: «What are we working on?», «What can go wrong?», «What are we going to do about it?», «Did we do a good job?». Это не разовый документ ради галочки, а способ думать: описать систему, найти угрозы, назначить решения, проверить себя — и пересматривать при изменениях.
Вопрос 1: что строим — схема потоков и границы доверия. Начинают с диаграммы потоков данных: кто с кем говорит, где хранятся данные. Ключевое понятие — граница доверия: место, где данные переходят с одного уровня доверия на другой (интернет → прокси, прокси → приложение, приложение → БД). Именно на границах чаще всего и возникают угрозы, потому что по одну сторону данным доверять нельзя. Здесь же перечисляют активы (что защищаем) и точки входа (откуда в систему можно попасть).
Вопрос 2: что может пойти не так — STRIDE. Чтобы не полагаться на интуицию «что вспомнилось», используют классификацию. STRIDE (Microsoft) — шесть классов угроз, и по каждому элементу схемы проходят все шесть:
| Класс | Угроза | Нарушает |
|---|---|---|
| Spoofing | подмена личности (выдать себя за другого) | подлинность |
| Tampering | искажение данных или кода | целостность |
| Repudiation | отказ от совершённого действия | неотказуемость |
| Information disclosure | утечка данных | конфиденциальность |
| Denial of service | отказ в обслуживании | доступность |
| Elevation of privilege | повышение привилегий | авторизация |
Ценность STRIDE — систематичность: пройдя все шесть классов по каждому элементу, находишь угрозы, о которых бы не вспомнил, — например, repudiation (нет журналов, некому доказать, кто что сделал) замечают редко, пока не пройдут по списку.
Вопрос 3: что сделаем — четыре решения на угрозу. Каждой найденной угрозе назначают одно из: снизить (митигировать — добавить контроль), принять (риск мал, живём с ним осознанно), передать (страховка, чужой сервис) или устранить (убрать саму возможность). Важно: решение — это действие с владельцем и сроком, а не «надо бы». Угроза без решения — это не учтённая угроза.
Вопрос 4: достаточно ли — проверка себя. Прошли ли STRIDE по всем элементам, у каждой ли угрозы есть решение, совпадает ли поверхность атаки со схемой. И главное — модель живёт: новая зависимость или новый вход означают новую угрозу, и модель пересматривают (та же мысль, что «однократно построенная надёжность деградирует» из 17.09).
Поверхность атаки перечисляют командами, а не по схеме — и она всегда шире. Это главный практический навык урока. Схема рисует «пользователь → прокси → приложение», но фактическая поверхность атаки — это то, что реально доступно, и её надо снять инструментами. На учебном стеке (обратный прокси + приложение) перечисление дало:
- опубликованные порты: не один (прокси :8080), а два — приложение оказалось опубликовано напрямую на :18000.
nmapподтвердил оба открытыми; - прямой обход точки контроля:
curlнапрямую в приложение :18000 вернул 200 — то есть все проверки прокси обходятся; штатный вход :8080 тоже 200. Это угроза класса spoofing/EoP: запрос минует контроль; - кто внутри контейнеров: приложение работает от uid 10001 (non-root, хорошо), а прокси — от root (побег из контейнера опаснее, 08.01/08.04);
- секреты в переменных окружения: нашлись
DB_PASSWORD(утечка, класс information disclosure — виден черезdocker inspect, 07.03) и, неожиданно,GPG_KEY— унаследованный от базового образа python.
Находка — не всегда секрет: нужен триаж. GPG_KEY в примере — это не утечка: базовый образ python кладёт в окружение отпечаток публичного ключа, которым проверял свою сборку, и он не секретен. А DB_PASSWORD — настоящий секрет. Автоматический поиск по слову «key» поднимает оба, и решает человек: это чувствительное или нет. Отсюда правило: перечисление даёт кандидатов, а не приговор; каждую находку триажат по факту. И отдельный урок: в окружение приезжает то, что вы не клали, — наследие базовых образов (та же находка, что в 07.02/08.10), и это тоже часть поверхности атаки.
Риск = вероятность, умноженная на ущерб; чинить всё нельзя. Угроз всегда больше, чем рук. Приоритизируют по риску: насколько вероятна атака и насколько велик ущерб. Прямой обход прокси и секрет в окружении — высокий риск (легко и больно), их чинят первыми; теоретическая угроза, требующая доступа, которого ни у кого нет, — можно принять. Это тот же прагматизм, что бюджет ошибок в 17.02: цель не «абсолютная защита» (недостижима и разорительна), а осознанное управление риском.
Что защищаем — триада CIA. Определение OWASP говорит «protecting something of value»; это «ценное» раскладывается на три свойства, и STRIDE бьёт ровно по ним. Конфиденциальность (данные видит только тот, кому положено) — по ней бьёт information disclosure. Целостность (данные и код не искажены) — tampering. Доступность (сервис работает, когда нужен) — denial of service. Плюс подлинность (против spoofing), неотказуемость (против repudiation) и авторизация (против elevation of privilege). Полезно, определяя активы, сразу помечать, какое из свойств для каждого важнее: для пароля БД это конфиденциальность, для журнала аудита — целостность и неотказуемость, для самого сервиса — доступность. Это подсказывает, какие угрозы для актива опаснее, и куда тратить силы.
Кто и зачем атакует — почти всегда не про вас лично. Подавляющее большинство атак оппортунистические: автоматические сканеры непрерывно обходят интернет и пробуют известные уязвимости и слабые пароли на всём подряд. Им неважно, кто вы, — важно, что дверь открыта. Именно поэтому базовая гигиена так эффективна: она закрывает дешёвые двери, а на дорогую целевую атаку большинство систем просто не окупает усилий атакующего. Целевые атаки (конкретно на вашу организацию) реже, дороже и требуют другого уровня защиты — но начинать с них, не закрыв массовое, бессмысленно. Отсюда порядок: сначала то, что ломают массово и дёшево (открытые порты, дефолтные пароли, непропатченные CVE), потом редкое и дорогое.
DevSecOps — безопасность внутри конвейера, а не аудит раз в год. Классическая модель «разработали, потом служба безопасности проверила перед релизом» не работает на скорости CI/CD: проверка в конце либо тормозит выкат, либо её пропускают. DevSecOps сдвигает проверки влево — в конвейер (блок 09), чтобы они шли автоматически при каждом изменении и были общей ответственностью, а не только службы безопасности. Конкретно в конвейер уезжает: сканирование зависимостей на уязвимости (SCA) и статический анализ кода (SAST) — 18.05; сканирование образов и SBOM — 18.04, 18.05; проверка политик как кода перед применением в кластер — 18.07; поиск секретов в коде — 18.05. Модель угроз при этом — вход: она говорит, какие проверки вообще нужны этому сервису. Автоматизация не заменяет мышление, но снимает с человека рутину и не даёт забыть.
Базовая гигиена — дешёвый минимум, отсекающий массовые атаки. До сложных средств защиты идут простые правила, каждое из которых закрывает целый класс угроз: наименьшие привилегии (non-root, минимальные права IAM/RBAC, MFA — 18.02, 18.06); минимизация поверхности (только нужные порты, нет обходов — 18.01, 18.08); секреты вне кода и окружения (хранилище, ротация — 18.03); обновления и цепочка поставок (закреплять и сканировать — 18.04, 18.05, 18.09); аудит (журналы доступа — 18.10); защита в глубину (несколько линий обороны) и безопасные умолчания (запрещено по умолчанию, разрешено явно — 14.05, 18.07). Большинство реальных взломов эксплуатируют отсутствие именно гигиены, а не хитрые уязвимости.
Защита в глубину — несколько линий, потому что каждая пробиваема. Ни один контроль не абсолютен: сеть настроят с ошибкой, в приложении найдут баг, секрет однажды утечёт. Поэтому защиту строят слоями, чтобы пробитие одного не открывало всё. На учебном стеке это выглядит так: сеть (приложение не опубликовано наружу, вход только через прокси — 18.08), затем права (приложение работает от non-root, чтобы побег из контейнера дал меньше — 18.06), затем политики (кластер не пускает привилегированные поды — 18.07), затем секреты (пароль не в коде, а в хранилище — 18.03), затем аудит (даже при успешной атаке останется след — 18.10). Атакующему, чтобы дойти до данных, нужно пробить каждый слой; защищающемуся достаточно, чтобы сработал хотя бы один. Обратная ошибка — «одна большая стена»: настроили периметр и успокоились, а внутри всё открыто (плоская сеть, root везде, секреты в окружении) — пробитие периметра означает полный доступ. Модель угроз помогает увидеть, где слой всего один, и это — приоритет на добавление второго.
3.1. Примеры и варианты реализации
Модель угроз — методология, она не зависит от среды; различается, что именно попадает в поверхность атаки.
| Среда | Что в поверхности атаки | Чем перечислять |
|---|---|---|
| Локальный стенд (Docker) | опубликованные порты, образы, окружение, root в контейнере | docker ps/inspect, nmap (демо урока) |
| VPS / bare-metal | открытые порты узла, службы, пользователи, ssh, обновления | ss/nmap, systemctl, аудит ssh (01, 18.06) |
| Yandex Cloud | публичные адреса и бакеты, права SA, метаданные ВМ, security groups | yc, инвентаризация (11.02, 11.04, 11.06) |
Локальный стенд. Демо перечисляет поверхность атаки docker-стека командами и находит расхождение со схемой (прямой обход, секрет в окружении, root).
VPS / bare-metal. Поверхность — это открытые порты узла и службы; перечисляют ss/nmap, проверяют ssh и учётные записи. Подробнее — в 18.06 (харденинг).
Yandex Cloud. В облаке поверхность иная: публичные адреса и открытые бакеты, чрезмерные права сервисных аккаунтов, доступ к сервису метаданных ВМ, широкие security groups. Перечисляют инвентаризацией облака (11.02, 11.04, 11.06). Замером в этом уроке не проверялось.
Что выбрать. Метод один в любой среде: описать систему и границы доверия, пройти STRIDE, назначить решения, перечислить фактическую поверхность и сверить со схемой. Меняется только инструмент перечисления и состав поверхности.
4. Практика на занятии (30 минут)
Курс проходят самостоятельно, поэтому каждый шаг говорит, что ввести (или сделать), зачем, что должно
получиться и что делать при неудаче. Стенд и материалы — в
examples/18.01-threat-model/.
Стенд — два контейнера в общей docker-сети (
course-obs-app:1как приложение и обратный проксиnginx:1.30-alpine), без kind и Kubernetes: предмет урока — методология построения модели угроз, а не оркестрация. Скриптlab-threatmodel.shподнимает стек намеренно с несколькими огрехами (описаны прямо в его коде) — их предстоит найти перечислением поверхности атаки, а не подсмотреть заранее.
Шаг 1 (8 минут). Поднять стек и перечислить поверхность атаки.
- Что ввести:
cd blocks/18-security/examples/18.01-threat-model
./lab-threatmodel.sh up
./lab-threatmodel.sh surface
- Зачем: схема рисует «пользователь → прокси → приложение», а фактическая поверхность атаки — это то, что реально доступно; расхождение между ними видно только перечислением командами, а не по рисунку.
- Ожидаемый результат:
upпечатает «стек поднят: прокси на :8080, приложение (напрямую!) на :18000».surfaceвыводит пять разделов: два открытых порта (nmapподтверждает обаopen), код200и у прямого обращения к приложению на :18000, и у обращения через прокси на :8080 (то есть контроль прокси обходится), образы обоих контейнеров без закреплённого digest, пользователя10001внутри приложения иrootвнутри прокси, и в конце — строкиНАЙДЕН: DB_PASSWORD=...иНАЙДЕН: GPG_KEY=...среди переменных окружения. - Если не так: если
surfaceне находит открытые порты — стек не поднялся, проверьтеdocker ps --filter name=lab1601и запуститеupзаново; еслиnmapне установлен на хозяине, запускайте шаг в контейнере с сетевыми утилитами (блок 04,course-net:13). - Ответьте: сколько точек входа оказалось на самом деле против одной на схеме? Что из найденного нарушает контроль, а что — просто небрежность (незакреплённый тег образа)?
Шаг 2 (5 минут). Триаж секретов.
- Что сделать: разберите две находки из переменных окружения —
DB_PASSWORDиGPG_KEY— и решите для каждой, секрет это или нет. - Зачем: автоматический поиск по слову «key»/«secret» поднимает кандидатов, а не приговор; решает человек, и ошибка в обе стороны дорога — пропущенный секрет остаётся утечкой, а ложная тревога тратит время реагирования.
- Ожидаемый результат:
DB_PASSWORD— настоящий секрет (пароль приложения, виден черезdocker inspect),GPG_KEY— не секрет, а отпечаток публичного ключа, которым базовый образpythonпроверял свою сборку. - Если не так: если оба помечены как секреты — проверьте, ссылается ли значение на что-то приватное
(пароль, токен) или на публично проверяемый факт (отпечаток ключа, версия); если сомневаетесь —
посмотрите, унаследована ли переменная из базового образа (
docker historyтого же базового образа без вашего слоя). - Ответьте: по какому признаку вы отличили секрет от несекрета? Откуда в окружении взялся
GPG_KEY, которого вы не задавали?
Шаг 3 (10 минут). Заполнить модель угроз по STRIDE.
- Что сделать: откройте
threat-model.md, пройдите STRIDE по каждому элементу схемы (пользователь, прокси, приложение, БД) и назначьте каждой найденной угрозе решение — снизить, принять, передать или устранить — с владельцем. - Зачем: угроза без решения — это не учтённая угроза; STRIDE даёт систематичность, без которой забывают классы вроде repudiation (нет журналов — некому доказать, кто что сделал).
- Ожидаемый результат: в
threat-model.mdзаполнены все шесть классов STRIDE минимум по одному примеру на класс (используйте находки шага 1: обход прокси — spoofing/elevation of privilege, секрет в окружении — information disclosure, root в прокси — elevation of privilege), и у каждой строки стоит конкретное решение с владельцем, а не «надо бы». - Если не так: если для какого-то класса STRIDE нет ни одной угрозы — вернитесь к схеме и подумайте, что по этому классу могло бы пойти не так именно с этим элементом, а не пропускайте класс.
- Ответьте: для какой угрозы вы выбрали «принять», а не «снизить», и почему это осознанный выбор, а не лень?
Шаг 4 (5 минут). Сверить чек-лист базовой гигиены.
- Что сделать: пройдите
hygiene-checklist.mdпо пунктам и отметьте, какие из них стек нарушает. - Зачем: базовая гигиена — дешёвый минимум, отсекающий массовые атаки; прежде чем думать о сложных средствах защиты, стоит убедиться, что закрыты именно эти дыры.
- Ожидаемый результат: отмечены минимум три нарушения — «работает не от root» (прокси — root),
«нет прямого доступа в обход точки контроля» (:18000 в обход прокси), «секретов нет в переменных
окружения» (
DB_PASSWORDвdocker inspect). - Если не так: если нарушений не нашлось — вы не сверяли пункт с фактом (командой), а поверили описанию стека на словах; вернитесь к выводу шага 1 и сверяйте каждый пункт буквально.
- Ответьте: какое из нарушенных правил гигиены закрыло бы сразу два найденных огреха?
Шаг 5 (2 минуты). Убрать за собой.
- Что ввести:
./lab-threatmodel.sh down
- Зачем: контейнеры и сеть стенда не удаляются сами и остаются занимать порты и ресурсы машины.
- Ожидаемый результат: строка «убрано»;
docker ps -a --filter name=lab1601ничего не показывает. - Если не так: если контейнеры остались — удалите вручную
docker rm -f lab1601-app lab1601-proxyиdocker network rm lab1601-net.
Проверка результата. Вы перечислили поверхность атаки командами, нашли расхождение со схемой, отличили секрет от несекрета и заполнили модель угроз с решениями.
5. Домашняя работа (1–2 часа)
Задание. Построить модель угроз для своей системы (или учебного стека) по четырём вопросам и STRIDE, перечислить её поверхность атаки и составить план устранения по риску.
Критерии приёмки.
- нарисована схема потоков данных с границами доверия, перечислены активы и точки входа;
- пройден STRIDE по каждому элементу схемы (все шесть классов), угрозы записаны;
- каждой угрозе назначено решение (снизить/принять/передать/устранить) с владельцем и сроком;
- поверхность атаки перечислена командами и сверена со схемой; расхождения отмечены;
- проведён триаж находок (секрет или нет) хотя бы на одном примере;
- угрозы приоритизированы по риску (вероятность и ущерб), названы те, что чинят первыми;
- пройден чек-лист базовой гигиены, нарушенные пункты отмечены.
Файлы для работы: blocks/18-security/examples/18.01-threat-model/
6. Частые ошибки и подводные камни
- Моделируют по схеме, а не по факту. Симптом: в модели один вход, в проде — три. Причина: не перечислили поверхность командами. Что делать: снимать surface инструментами.
- Проходят только очевидные угрозы. Симптом: repudiation и EoP не рассмотрены. Причина: полагались на интуицию. Что делать: STRIDE по каждому элементу, все шесть классов.
- Угроза без решения. Симптом: список угроз есть, ничего не изменилось. Причина: нет действий с владельцем. Что делать: каждой угрозе — решение и срок.
- Всякую находку считают секретом. Симптом: паника из-за
GPG_KEY, который публичный. Причина: нет триажа. Что делать: перечисление даёт кандидатов, решает человек. - Чинят всё подряд. Симптом: недели на теоретические угрозы, реальные открыты. Причина: нет приоритизации. Что делать: риск = вероятность на ущерб, сначала высокий.
- Модель пишут один раз. Симптом: через полгода не отражает систему. Причина: не пересматривают. Что делать: обновлять при изменении системы.
- Гигиену считают «слишком простой». Симптом: дорогой WAF при секрете в коде. Причина: пропустили базу. Что делать: сначала гигиена, потом сложное.
7. Проверка усвоения
- Назовите четыре вопроса модели угроз.
- Что такое граница доверия и почему угрозы концентрируются на ней?
- Расшифруйте STRIDE и приведите пример угрозы каждого класса.
- Почему поверхность атаки перечисляют командами, а не по схеме? Что нашлось на стенде?
- Чем находка отличается от секрета? Приведите пример триажа.
- Как приоритизировать угрозы, если чинить все нельзя?
- Перечислите правила базовой гигиены и класс угроз, который закрывает каждое.
- Что такое DevSecOps и как он связан с конвейером (блок 09)?
8. Материалы
Минимум два варианта на выбор: официальная документация плюс книга/статья/видео.
- OWASP: Threat Modeling — owasp.org/www-community/Threat_Modeling. Определение и четыре вопроса — первоисточник урока.
- OWASP Threat Modeling Cheat Sheet — cheatsheetseries.owasp.org/cheatsheets/Threat_Modeling_Cheat_Sheet.html. Практическое руководство по процессу.
- Microsoft: STRIDE (The STRIDE Threat Model) — learn.microsoft.com/en-us/azure/security/develop/threat-modeling-tool-threats. Классификация угроз STRIDE.
← Предыдущий: 17.09. Chaos engineering и учебные аварии · Блок 18 · Следующий: 18.02. Управление доступом: наименьшие привилегии, IAM, MFA, SSO →
Материалы для практики
Скрипты, шпаргалки и заготовки к заданиям урока. Текст урока — на этой странице.
-
Материалы к уроку 18.01 «Модель угроз и базовая гигиена инфраструктуры».zip