Чек-лист безопасности часто начинается с контроля: включить подпись, шифрование, 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 без актива не показывает, что именно доказано.
| Поле | Учебное значение | Зачем оно нужно | Чего оно не доказывает |
|---|---|---|---|
| Actor | browser-client | указывает источник входа | личность пользователя или тип устройства |
| Asset | change-request | даёт объекту защиты имя | классификацию данных и законность обработки |
| Trust boundary | public-api-to-handler | ставит проверку на смене предположения | топологию настоящей сети |
| Abuse | request without valid signature | делает риск проверяемым условием | полный перечень атак |
| Control | signed-request | связывает действие с условием | качество реализации подписи |
| Evidence | rejection 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, а не неявной строки.
Почему контроль не открывает разговор
NIST SP 800-154 в статусе initial public draft описывает threat modeling как форму risk assessment и отдельно рассматривает data-centric подход. Его полезный для практики вывод не «берите один перечень мер», а сначала моделируйте то, что важно защищать, и стороны атаки и защиты. Документ не завершает работу за команду: он не знает ваш актив, роль, протокол и допустимое доказательство. Поэтому ссылка на него не превращает таблицу выше в сертификационный артефакт.
NIST SP 800-218 v1.1 говорит о практиках безопасной разработки, которые можно встроить в жизненный цикл. Это хорошее место для следующего шага после модели: договориться, где хранится запись угрозы, кто проверяет evidence и как изменение возвращается назад. Но framework не назначает signed-request правильным ответом для любого потока. Сначала граница, затем риск, затем решение, затем доказательство и владелец проверки.
Маршрут: симптом → причина → проверка → действие
- Симптом. Найдите один чек-листовый пункт, у которого нет ответа на вопрос «какой актив он защищает». Не расширяйте задачу до всего продукта.
- Причина. Нарисуйте один поток с source, process, store и trust boundary. Если границу нельзя назвать словами, пока нельзя выбрать осмысленную проверку.
- Проверка. Запустите
node web/scripts/upgrade-2023-01.mjs --verify-fixture. PASS подтверждает только assertions учебной модели: обязательные поля, связь control/evidence и rollback snapshot. - Действие. Для одного злоупотребления запишите один control и evidence. Evidence должен быть наблюдаемым: отказ, лог решения, unit test или согласованная ручная процедура — не обещание «всё закрыто».
- Ревью. Попросите другого инженера прочитать запись, закрыв название технологии. Если он не понимает актив и условие отказа, технология была выбрана слишком рано.
- Откат. При спорном новом контроле верните 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, а все три источника задают методические рамки, не вывод о безопасности конкретного приложения.
Проверяемые источники
- NIST SP 800-154, Initial Public Draft: Guide to Data-Centric System Threat Modeling, 14 марта 2016 — официальный датированный initial public draft, доступный к январю 2023. Это не финальная публикация; он задаёт data-centric взгляд на модель угроз, а не аттестует систему.
- NIST SP 800-218 SSDF Version 1.1, Final, 3 февраля 2022 — официальный финальный документ, доступный к январю 2023. Он описывает практики безопасной разработки; не доказывает, что конкретный код им соответствует.
- NIST SP 800-53 Revision 5, Security and Privacy Controls, September 2020 — неизменяемый официальный PDF. Каталог помогает назвать тип контроля и evidence, но сам по себе не выбирает контроль для конкретного потока.