DarkRiDDeR11 мин

Модель угроз перед чек-листом: назвать поток, актив и границу

БезопасностьРазработка

Чек-лист безопасности часто начинается с контроля: включить подпись, шифрование, rate limit или сканер. Проблема появляется позже: никто не может сказать, что именно защищает выбранный пункт и на какой границе он действует. Цена не в том, что список стал короче. Команда получает изменение, которое нельзя проверить по смыслу: отказ в доступе есть, а актив, от которого отказ должен защищать, так и не назван.

Для начала не нужен большой аудит. Возьмём один учебный поток: browser-client отправляет change-request через границу public-api-to-handler, обработчик кладёт запрос в store с тем же именем. Рядом фиксируем предполагаемое злоупотребление: отправить запрос без корректной подписи. Это не карта реальной системы, не security review и не аттестация. Цель скромнее: не позволить обсуждению перескочить от тревожного слова «атака» сразу к чужому контролю.

Четыре имени до выбора контроля

Актив — не «данные вообще», а конкретный объект, потеря или искажение которого имеет значение для этого потока. В примере это change-request. Граница доверия — не название сервиса и не цвет на диаграмме; это место, где мы меняем предположение о входе. Здесь строка public-api-to-handler означает, что handler не принимает данные клиента как уже проверенные. Злоупотребление — действие, которое нарушает нужное свойство: прислать запрос без valid signature.

После этих имён появляется место для контроля. В учебной модели signed-request означает только план: принять запрос после проверяемой подписи. Он не раскрывает алгоритм, заголовок, ключ, clock skew или формат ошибки. Такие детали нельзя честно вывести из одной DFD-строки. Зато можно заранее назвать evidence: локальная проверка, что неподписанный учебный запрос отклонён. Контроль без такого evidence остаётся намерением, а evidence без актива не показывает, что именно доказано.

Минимальная запись одного потока
ПолеУчебное значениеЗачем оно нужноЧего оно не доказывает
Actorbrowser-clientуказывает источник входаличность пользователя или тип устройства
Assetchange-requestдаёт объекту защиты имяклассификацию данных и законность обработки
Trust boundarypublic-api-to-handlerставит проверку на смене предположениятопологию настоящей сети
Abuserequest without valid signatureделает риск проверяемым условиемполный перечень атак
Controlsigned-requestсвязывает действие с условиемкачество реализации подписи
Evidencerejection in local fixtureфиксирует ожидаемую отрицательную веткурезультат pentest или production trace

Проверяемый пример: контракт, а не сканер

Пример ниже работает в Node с простыми строками. Он не отправляет HTTP-запрос, не создаёт ключ, не вызывает криптографическую библиотеку и не знает настоящую конфигурацию. Поэтому его результат повторяем, но узок. PASS fixture говорит, что локальная функция требует actor, asset, boundary и abuse, связывает один control с одним evidence и умеет вернуть исходный snapshot. Он не говорит «endpoint безопасен».

const plan = planTeachingThreatModel({
  actor: 'browser-client',
  asset: 'change-request',
  boundary: 'public-api-to-handler',
  abuse: 'send request without a valid signature',
  control: 'signed-request',
});

console.log(plan.accepted);             // true
console.log(plan.controls[0].id);       // signed-request
console.log(plan.evidence[0]);
// проверка отказа неподписанного учебного запроса

const rollback = rollbackTeachingThreatModel(plan);
console.log(rollback.snapshot.boundary); // public-api-to-handler

Отрицательная ветка здесь важнее красивого happy path. Если asset пустой, модель возвращает missing-asset; если не названа boundary — missing-boundary. Незнакомый control encrypt-everything даёт unknown-control, а не догадку о смысле. Это защищает ревью от трёх привычных ошибок: назвать контроль вместо цели, пропустить место решения и закрыть неясность модным словом. Добавление нового контроля должно потребовать явного правила и нового evidence, а не неявной строки.

Учебный поток модели угроз: browser-client пересекает границу public-api-to-handler, handler пишет change-request в store; рядом показано злоупотребление неподписанным запросом, контроль signed-request и evidence отказа.
Схема фиксирует проектный DFD-контракт одного потока. Она не отражает реальную сеть, пользователей, ключи, журналирование или результаты security review.

Почему контроль не открывает разговор

NIST SP 800-154 в статусе initial public draft описывает threat modeling как форму risk assessment и отдельно рассматривает data-centric подход. Его полезный для практики вывод не «берите один перечень мер», а сначала моделируйте то, что важно защищать, и стороны атаки и защиты. Документ не завершает работу за команду: он не знает ваш актив, роль, протокол и допустимое доказательство. Поэтому ссылка на него не превращает таблицу выше в сертификационный артефакт.

NIST SP 800-218 v1.1 говорит о практиках безопасной разработки, которые можно встроить в жизненный цикл. Это хорошее место для следующего шага после модели: договориться, где хранится запись угрозы, кто проверяет evidence и как изменение возвращается назад. Но framework не назначает signed-request правильным ответом для любого потока. Сначала граница, затем риск, затем решение, затем доказательство и владелец проверки.

Маршрут: симптом → причина → проверка → действие

  1. Симптом. Найдите один чек-листовый пункт, у которого нет ответа на вопрос «какой актив он защищает». Не расширяйте задачу до всего продукта.
  2. Причина. Нарисуйте один поток с source, process, store и trust boundary. Если границу нельзя назвать словами, пока нельзя выбрать осмысленную проверку.
  3. Проверка. Запустите node web/scripts/upgrade-2023-01.mjs --verify-fixture. PASS подтверждает только assertions учебной модели: обязательные поля, связь control/evidence и rollback snapshot.
  4. Действие. Для одного злоупотребления запишите один control и evidence. Evidence должен быть наблюдаемым: отказ, лог решения, unit test или согласованная ручная процедура — не обещание «всё закрыто».
  5. Ревью. Попросите другого инженера прочитать запись, закрыв название технологии. Если он не понимает актив и условие отказа, технология была выбрана слишком рано.
  6. Откат. При спорном новом контроле верните snapshot договора и старую ветку обработки; не используйте rollback как утверждение, что уже развернутая система вернулась в безопасное состояние.

Граница модели и следующий шаг

Эта малая DFD не ранжирует угрозы, не считает вероятность, не строит attack tree, не перечисляет trust zones продукта и не проверяет реализацию подписи. В ней нет реальных запросов, дат, пользователей, журналов или эффекта. Если в проекте есть требования к классификации, ключам, персональным данным или независимой оценке, они потребуют других артефактов и владельцев. Подменять их одной функцией было бы опаснее, чем не иметь функции вовсе.

Следующий шаг — взять один настоящий change и заполнить те же шесть полей на листе ревью. После этого выбрать только тот unit или integration test, который способен дать названный evidence. Так threat model остаётся рабочей записью о решении, а не приложением к чек-листу. Для каждой новой границы повторите путь заново: имя актива, злоупотребление, контроль, evidence, условие отката.

Историческая граница января 2023

Использованы официальные документы, существовавшие к январю 2023: NIST SP 800-154 IPD от 14 марта 2016 года, NIST SP 800-218 v1.1 Final от 3 февраля 2022 года и NIST SP 800-53 Rev. 5 от 2020 года. Первый назван draft, а все три источника задают методические рамки, не вывод о безопасности конкретного приложения.

Проверяемые источники