DarkRiDDeR13 мин

Контроль и evidence в модели угроз: одна связь, которую можно проверить

БезопасностьАрхитектура

В обсуждении безопасности легко получить два несвязанных списка: слева угрозы, справа controls. Между ними нет правила, поэтому review принимает любое знакомое слово. Проблема проявляется при изменении: control остаётся в коде, но никто не знает, какая отрицательная ветка должна доказать его работу. Цена — ложное закрытие риска и трудный откат: нельзя понять, какую гарантию потеряем, если удалить изменение.

Разберём связь на одной учебной модели. Актив change-request приходит от browser-client и пересекает public-api-to-handler. Злоупотребление — передать запрос без valid signature. Control signed-request обещает ровно одно: handler не принимает вход без проверяемой подписи. Evidence — результат локальной проверки отказа для специально заданного неподписанного значения. Модель не реализует подпись и не говорит, что у продукта есть такой endpoint. Она показывает, как записать связь до реализации.

DFD здесь нужен как граница предположений

Data flow diagram часто пытаются превратить в архитектурную картину на все случаи. Для малого review достаточно четырёх сущностей: source, flow, process и store. На стрелке между source и process лежит trust boundary: после неё нельзя считать вход уже доверенным. Store не обязан быть базой данных; в учебной записи это только место, где оказался asset. Такое упрощение полезно, пока оно честно: мы не показываем протокол, изоляцию сети, репликацию или журнал.

У угрозы тоже должен быть глагол. «Подделка» слишком широка, пока не сказано, что делает актор и какое свойство актива нарушает. Формулировка «send request without a valid signature» даёт отрицательный пример. Проверка может установить входное условие без подписи и ожидать отказ по договору handler. Она не доказывает отсутствие всех способов подделки, не проводит криптоанализ и не заменяет тест границ инфраструктуры.

Контракт связи threat → control → evidence
СлойЗапись в учебной моделиВопрос ревьюераНедопустимый вывод
Assetchange-requestчто потеряет смысл при нарушении?что защищены все данные продукта
Threatunsigned requestкакое действие пытаемся остановить?что перечислены все злоупотребления
Controlsigned-requestкакую точку решения меняем?что выбранный механизм достаточен
Evidencelocal rejection checkкакой наблюдаемый отказ ждём?что выполнен security test среды
Rollbackrestore snapshotчто вернётся в учебной записи?что production rollout безопасно отменён

Почему один control должен иметь названный evidence

Контроль иногда действительно требует нескольких проверок, но начинать полезно с минимальной пары. Если для signed-request нельзя назвать хотя бы одну отрицательную ветку, команда ещё не понимает его контракт. Если evidence — только «код прочитан», оно может быть разумным для дизайна, но не должно называться доказательством поведения. Хорошая запись разделяет эти виды: design review проверяет выбор границы, unit test проверяет локальный reject, а внешний security assessment имеет другой объект и критерий.

NIST SP 800-53 Rev. 5 — каталог controls, а не генератор тестов. Его роль в этой статье ограничена словарём: security control имеет функцию и нуждается в уверенности, что функция работает. Из этого не следует, что любой control стоит тащить в маленький сервис или что имя control заменяет спецификацию. Для выбранного потока достаточно сделать связь видимой, затем спросить владельца домена, нужна ли она вообще.

Исполнимый контракт и его отрицательные ветки

Функция planTeachingThreatModel принимает только заранее названные строки. Пустой asset, отсутствующая boundary и неизвестный control отклоняются. Это намеренная строгость учебной модели: ей запрещено достраивать пропущенный контекст. Объект результата содержит dfd, threat, control и evidence, а также snapshot для rollback. Никаких скрытых глобальных данных нет, поэтому fixture повторяем на любой машине с Node.

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

Rollback важен не как кнопка «безопасно отменить». Он проверяет другое: при отмене проекта контроля мы возвращаем исходные actor, asset, boundary и abuse, не подставляя новую историю. В production отмена может задеть ключи, миграции, compatibility, очередь, документацию и пользователей. Наша функция ничего из этого не делает. Она не должна даже имитировать deployment, иначе у читателя возникнет ложное чувство, что unit fixture покрывает операционный риск.

Контракт учебной модели: asset change-request и threat unsigned request связываются с control signed-request и evidence local rejection check; отсутствующий asset и неизвестный control ведут в reject; rollback возвращает исходный snapshot.
Схема показывает отношения объектов модели и отрицательные ветки. Это не диаграмма криптографического протокола, не журнал реального приложения и не отчёт о тестировании.

Откуда в этом месте появляется разработка

SSDF v1.1 полезен тем, что связывает design, implementation и verification в практиках разработки. Но в конкретном PR эти общие слова надо развернуть. Design: назвать boundary и abuse. Implementation: указать место, где handler принимает или отклоняет вход. Verification: выбрать test, который делает отрицательную ветку наблюдаемой. Change management: записать rollback и условие его запуска. Такой порядок не гарантирует отсутствие дефекта, но делает спор предметным.

Не нужно объявлять every validation security control. Иногда входной фильтр обслуживает только UX или форматирование. Связь с угрозой возникает, когда есть asset, недопустимое действие и ожидаемое защитное решение. И наоборот, не вся угроза обязана закрываться кодом process: часть требует изменения boundary, прав, операционной процедуры или отказа от функции. Модель нужна именно для различения этих случаев.

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

  1. Симптом. В PR есть control, но нет формулировки, какой негативный вход или переход он остановит.
  2. Причина. Разделите слова на asset, abuse, control и evidence. Если одно поле невозможно заполнить, не компенсируйте это ещё одним middleware.
  3. Проверка модели. Выполните fixture. Отдельно посмотрите, что assertions о missing asset, missing boundary и unknown control остаются true: они защищают договор от молчаливого угадывания.
  4. Действие в коде. Выберите точку принятия решения, добавьте отрицательный test именно для неё и назовите evidence в описании изменения.
  5. Проверка границы. Разберите, кто формирует вход до boundary и кто владеет отказом после неё. Не объявляйте local test проверкой proxy, identity provider или key management.
  6. Откат. До merge запишите, какой контракт и test уходят вместе с control. Если откат требует другой миграции или ключа, остановитесь и сделайте отдельный план выпуска.

Граница утверждений

Учебный контракт не применяет подпись, не обрабатывает временные окна, не хранит секреты, не проверяет права и не показывает настоящий data store. Он также не ранжирует риск и не отвечает, нужен ли signed-request в конкретном дизайне. Его единственный измеримый результат — тринадцать assertions о созданных в памяти объектах. Называть это threat modeling процесса можно только с уточнением «учебная малая модель», а не «мы провели security review».

Следующий шаг — заменить строки одним реальным контрактом команды: сделать asset и boundary именами из вашей документации, а evidence — ссылкой на разрешённый test или review. Не переносите в статью или ticket секреты, реальные request samples и имена пользователей. Достаточно того, чтобы независимый reviewer мог понять, какой отказ ожидается и какую часть изменения нужно откатить при несоответствии.

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

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

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