Моделирование угроз: думать как защитник

Лучшее время найти дыру — до того, как написан код, на доске.

Threat modeling — структурированный разбор «что может пойти не так» в системе: какие активы есть, кто и как может им навредить и что мы с этим сделаем.

Зачем моделировать угрозы

Уязвимости дешевле всего убирать на этапе дизайна. Моделирование угроз — это дисциплина задавать четыре вопроса до написания кода: что мы строим? что может пойти не так? что мы с этим сделаем? хорошо ли мы это сделали? Полчаса у доски экономят недели разгребания инцидента.

Главная ценность упражнения не в красивой диаграмме, а в смене точки зрения. Разработчик по умолчанию думает о том, как система работает в нормальном сценарии: пользователь заполнил форму, нажал кнопку, получил результат. Моделирование угроз заставляет на время снять эти «розовые очки» и спросить иначе: а что, если пользователь враждебен? что, если он отправит не то, что я жду? что он может выиграть, сломав это? Такой разворот мышления — навык, который тренируется, и регулярная практика моделирования его и тренирует.

Ещё одна причина делать это рано — на дизайне доступны решения, которых уже не будет потом. Если на доске выясняется, что хранить номера карт целиком опасно и незачем, можно вообще не проектировать такое хранилище — и целый класс угроз исчезает. После того как код написан, базы заполнены, а интеграции запущены, тот же вывод оборачивается дорогой миграцией. Самые дешёвые контрмеры — архитектурные, а архитектуру выбирают в начале.

Шаг 1. Что мы строим

Нарисуйте поток данных: компоненты, хранилища, внешних акторов и стрелки между ними. Отметьте границы доверия — места, где данные переходят из менее доверенной зоны в более доверенную.

Не гонитесь за идеальной нотацией — подойдёт и набросок на салфетке. Важна не красота схемы, а полнота: ничего, что принимает или хранит данные, не должно остаться невидимым. Самые опасные угрозы часто прячутся именно в забытых стрелках — фоновой задаче, которую никто не нарисовал, служебном эндпоинте, очереди сообщений, кэше. Поэтому, рисуя поток, полезно спросить себя: откуда сюда ещё могут прийти данные, кроме главного сценария? Каждый найденный «теневой» вход — это место, которое иначе осталось бы без проверки. Хорошая диаграмма потока данных уже наполовину делает работу: она показывает, куда вообще смотреть.

[Пользователь] --HTTPS--> [API] --> [Сервис заказов] --> [БД]
                            |
                            +--> [Платёжный провайдер] (внешний)
 -- -- -- граница доверия -- -- -- (всё слева от API не доверенно)

Шаг 2. Что может пойти не так: STRIDE

STRIDE — мнемоника шести классов угроз. Пройдитесь по каждому элементу схемы и спросите про каждую букву.

Смысл мнемоники в том, чтобы не полагаться на вдохновение. Без чеклиста разбор угроз сводится к тому, что вспомнилось: обычно это пара очевидных пунктов, а остальное упускается. STRIDE даёт систематический проход — шесть вопросов к каждой стрелке и каждому хранилищу на схеме. Даже если по какой-то букве для данного элемента угроз не нашлось, само то, что вы её рассмотрели и осознанно отбросили, ценно: вы не пропустили целый класс по невнимательности, а приняли решение.

БукваУгрозаКонтрмера
S — Spoofingвыдать себя за другогоаутентификация, MFA
T — Tamperingподмена данныхподписи, контроль целостности
R — Repudiationотрицание действияаудит-логи
I — Information disclosureутечка данныхшифрование, контроль доступа
D — Denial of serviceотказ в обслуживанииrate limiting, квоты
E — Elevation of privilegeповышение правпроверка авторизации на сервере

Шаг 3. Что мы сделаем: ранжирование и контрмеры

Не все угрозы равны. Грубо оцените риск как сочетание вероятности и ущерба и беритесь сначала за высокие. Для каждой выбранной угрозы — конкретная контрмера: «утечка токенов сессии» → httpOnly-cookie + короткое истечение + ротация.

Ранжирование защищает от двух противоположных провалов. Первый — паника: команда видит длинный список угроз, пугается и пытается закрыть всё сразу, выдыхается на мелочах и не доводит до конца главное. Второй — паралич: список такой большой, что за него вообще не берутся. Грубая оценка риска расставляет приоритеты и делает работу выполнимой: предсказуемый идентификатор, открывающий доступ к чужим персональным данным, чинят сегодня, а маловероятную угрозу с ничтожным ущербом можно осознанно отложить и записать как принятый риск.

Важно, чтобы у каждой выбранной угрозы появились не только контрмера, но и владелец, и место, где это зафиксировано. Угроза, про которую все «вроде помнят», но никто конкретно не отвечает, тихо теряется между спринтами. Поэтому полезно сразу заводить задачу: что за угроза, какой риск, какая контрмера, кто делает. Так модель угроз превращается из разговора в работу, которую можно отследить и проверить.

Угроза: IDOR в /api/orders/{id} (чужой заказ по подмене id)
Вероятность: высокая (id предсказуем)
Ущерб: высокий (персональные данные)
Риск: ВЫСОКИЙ -> чинить в первую очередь
Контрмера: проверять на сервере, что order.user_id == current_user.id

Как работает под капотом: где это живёт в процессе

Модель угроз — не разовый документ, а живая практика. Лёгкая версия: на каждую новую фичу команда за 20–30 минут проходит DFD + STRIDE и заносит решения в задачу. Большие изменения архитектуры заслуживают отдельной сессии. Главное — регулярность, а не объём бумаги.

Опасная ловушка — превратить моделирование угроз в тяжёлый бюрократический ритуал с огромными шаблонами, которые заполняют раз в год «для галочки». Такой документ устаревает в день подписания и никем не читается. Живая практика выглядит иначе: короткая, частая, привязанная к реальному изменению. Лучше пятнадцать минут честного разговора у доски на каждую заметную фичу, чем стостраничный отчёт, оторванный от кода. Со временем команда нарабатывает интуицию, и многие угрозы отбраковываются почти на автомате — но именно регулярность приводит к этой интуиции.

Частые ошибки

  • Моделировать «идеального пользователя». Думайте о злоумышленнике и о честной ошибке одновременно.
  • Слишком поздно. Модель угроз после релиза почти бесполезна — поезд ушёл.
  • Список без действий. Угроза без назначенной контрмеры и владельца — просто страшилка.

Итоги

  • Четыре вопроса: что строим, что пойдёт не так, что сделаем, хорошо ли сделали.
  • STRIDE — чеклист из шести классов угроз для каждого элемента схемы.
  • Ранжируйте по риску (вероятность × ущерб) и закрывайте угрозы конкретными контрмерами.
Проверьте себя
1. За что отвечает буква E в STRIDE?
AEncryption — шифрование данных
BElevation of privilege — повышение привилегий
CError handling — обработка ошибок
DExposure — раскрытие портов
2. Когда моделирование угроз приносит наибольшую пользу?
AПосле инцидента в продакшене
BНа этапе дизайна, до написания кода
CТолько при сертификации
DРаз в год силами внешнего аудитора
3. Как разумно приоритизировать найденные угрозы?
AПо алфавиту букв STRIDE
BПо сочетанию вероятности и потенциального ущерба — сперва высокий риск
CСлучайным образом
DСначала самые простые в исправлении