diff --git a/editorial/agent-rewrites/179.json b/editorial/agent-rewrites/179.json index 175b49f..50c2b4d 100644 --- a/editorial/agent-rewrites/179.json +++ b/editorial/agent-rewrites/179.json @@ -1,7 +1,7 @@ { "index": 179, "slug": "editorial-2023-01-mechanism-threat-model", - "title": "Модель угроз без догадок: связать актив, риск и проверяемый контроль", - "excerpt": "Как превратить разговор о безопасности в проверяемую связь: назвать актив, границу и злоупотребление, выбрать контроль и заранее определить evidence для отрицательного пути.", - "contentHtml": "

В ревью появляется знакомый симптом: в изменении уже назван контроль — подпись, шифрование, rate limit или дополнительная проверка, — но никто не может ответить, какой актив он защищает и какой вход должен быть отвергнут. Обсуждение быстро сводится к вкусу: один инженер предлагает JWT, другой — mTLS, третий — ещё один middleware. Цена ошибки — ложное закрытие риска. Команда может потратить время на защиту не той границы, а при откате не сможет объяснить, какую гарантию она снимает.

\n

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

\n

Механизм: от актива к доказательству

\n

Возьмём учебный поток: browser-client отправляет change-request через границу public-api-to-handler, обработчик принимает или отклоняет запрос, затем записывает его в store. Актив — не «данные вообще», а конкретный change-request. Злоупотребление — отправить запрос без valid signature. Контроль — проверять подпись до записи. Evidence — наблюдаемый отказ на неподписанном входе.

\n

У каждого поля есть один вопрос. Asset отвечает: «что потеряет свойство при ошибке?». Abuse отвечает: «какое действие мы пытаемся остановить?». Boundary отвечает: «где вход перестаёт быть доверенным?». Control отвечает: «какое решение примет система?». Evidence отвечает: «что увидит проверяющий и по какому признаку скажет pass или fail?». Если поле заменяют словами «система», «безопасность» или «проверено», модель теряет смысл.

\n
Минимальная связь в одном потоке
ЭлементУчебное значениеПроверяемый вопросЧто не следует из записи
Actorbrowser-clientкто формирует вход?личность пользователя и его права
Assetchange-requestчто нужно сохранить?классификация всех данных продукта
Boundarypublic-api-to-handlerгде меняется доверие к входу?полная топология сети
Abuseunsigned requestкакое действие не должно пройти?полный список атак
Controlsigned-requestкакое условие проверяет handler?достаточность криптографии
Evidencelocal rejection checkкакой отказ можно наблюдать?результат production-проверки или pentest
\n

Конкретный пример: строгий контракт для отрицательного пути

\n

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

\n
function planThreatModel(input) {\n  const required = ['actor', 'asset', 'boundary', 'abuse', 'control', 'evidence'];\n  const missing = required.filter((key) => !input[key]);\n\n  if (missing.length > 0) {\n    return { status: 'reject', reason: `missing: ${missing.join(', ')}` };\n  }\n\n  const allowedControls = ['signed-request', 'explicit-authorization'];\n  if (!allowedControls.includes(input.control)) {\n    return { status: 'reject', reason: 'unknown-control' };\n  }\n\n  return {\n    status: 'accept',\n    contract: {\n      asset: input.asset,\n      abuse: input.abuse,\n      control: input.control,\n      evidence: input.evidence\n    }\n  };\n}\n\n// Учебный вызов: это не проверка реального endpoint.\nplanThreatModel({\n  actor: 'browser-client',\n  asset: 'change-request',\n  boundary: 'public-api-to-handler',\n  abuse: 'unsigned request',\n  control: 'signed-request',\n  evidence: 'local rejection check'\n});
\n

Положительный результат здесь означает только то, что запись содержит нужные поля и знакомый контроль. Он не означает, что подпись реализована правильно, ключи защищены, права проверены или запрос нельзя подделать другим способом. Отрицательный путь важнее happy path: пустой asset, отсутствующая boundary и неизвестный control должны остановить описание. В рабочем коде такой контракт нужно дополнить тестом самого handler, а затем отдельно проверить интеграцию и эксплуатационные границы.

\n
\"Учебная
Учебная схема показывает связь change-request, unsigned request, signed-request и local rejection check. Она не описывает реальную сеть, ключи или результаты проверки.
\n

Симптом → причина → проверка → действие

\n
Диагностика неполной модели
СимптомПричинаПроверкаДействие
Есть control, но нет assetвыбрали привычную технологиюкакой объект теряет свойство?назвать один asset до выбора механизма
Есть threat, но нет boundaryне указано место решениягде вход меняет статус доверия?показать одну границу на потоке
Есть control, но нет evidenceне описан отрицательный путькакой вход должен получить отказ?добавить test, log или ручную проверку
В evidence написано «проверено»не назван метод и результатчто именно увидит другой инженер?указать вход, ожидаемый отказ и артефакт
Rollback означает «вернуть всё»модель смешана с выпускомкакие поля и ресурсы реально меняются?разделить snapshot договора и план отката
\n

Порядок действий

\n
  1. Сузьте область. Возьмите один поток, один актив и одну границу. Не пытайтесь описать весь продукт одной диаграммой.
  2. Назовите злоупотребление глаголом. Формулировка «отправить запрос без действительной подписи» проверяемее, чем слово «подделка».
  3. Определите решение. Запишите, в какой точке handler должен принять или отклонить вход. Не выдавайте имя технологии за условие.
  4. Опишите evidence. Укажите конкретный вход, ожидаемый результат и артефакт: unit test, журнал решения или согласованную ручную проверку.
  5. Проверьте отрицательную ветку. Передайте пустой asset, неверный вход или неизвестный control. Система должна отказать явно, а не достроить контекст.
  6. Отделите модель от реализации. Сверьте контракт с реальным кодом, конфигурацией и владельцем ключей. Учебный пример не заменяет эти проверки.
  7. Запишите откат. Назовите, что возвращается при удалении контроля. Если меняются ключи, миграции, очереди или совместимость, нужен отдельный операционный план.
\n

Ограничения и отрицательный путь

\n

Малая модель не ранжирует риск, не считает вероятность, не строит attack tree и не перечисляет все trust zones. Она не проверяет криптографический протокол, identity provider, права доступа, хранение секретов или журналирование. Если актив связан с персональными данными, платежами или критичной операцией, потребуется более подробная классификация, согласованный владелец риска и независимая проверка.

\n

Контроль может оказаться неверным даже при полной записи. Подпись не заменяет авторизацию. Шифрование канала не доказывает целостность бизнес-операции. Rate limit не решает проблему подмены личности. Нельзя переносить учебное signed-request на реальный API без проверки протокола, ключей, времени жизни, повторной отправки и обработки ошибок. Отрицательный результат модели — повод остановиться и уточнить решение, а не повод подобрать другое модное слово.

\n

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

\n

Модель готова к следующему этапу, если независимый инженер без устных пояснений может назвать asset, злоупотребление, boundary, контроль и evidence; воспроизвести отрицательную проверку; увидеть однозначный pass или fail; и понять, что именно откатывается. Это критерий полноты записи, а не сертификат безопасности. Для реализации нужен отдельный критерий: тест должен пройти на настоящем handler с разрешённым входом и явно заданным неподписанным входом, а результат должен принадлежать согласованному артефакту проверки.

\n

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

\n" + "title": "Модель угроз: от актива к проверяемому контролю", + "excerpt": "Как связать актив, злоупотребление, границу и контроль в одну проверяемую модель — с учебным контрактом, отрицательным путём и явными ограничениями.", + "contentHtml": "

В ревью появляется знакомый симптом: в изменении уже назван контроль — подпись, шифрование, rate limit или дополнительная проверка, — но никто не может ответить, какой актив он защищает и какой вход должен быть отвергнут. Обсуждение быстро сводится к выбору технологии: один инженер предлагает JWT, другой — mTLS, третий — ещё один middleware. Цена ошибки — ложное закрытие риска. Команда может защитить не ту границу, а при откате не понять, какую гарантию она снимает.

\n

Модель угроз помогает вернуть разговор к проверяемой цепочке. Для одного потока нужно назвать участника, актив, границу, злоупотребление, контроль и evidence — наблюдаемое доказательство результата. Название технологии не заменяет ни одного из этих полей. Ниже — учебная модель, которая проверяет полноту записи, а не доказывает безопасность продукта.

\n

Как устроен минимальный контракт

\n

Возьмём учебный поток: browser-client отправляет change-request через public-api-to-handler, обработчик принимает или отклоняет запрос, затем записывает его в store. Актив — конкретный change-request, а не «данные вообще». Злоупотребление — отправить запрос без действительной подписи. Граница — место, где вход перестаёт быть доверенным. Контроль — решение handler до записи. Evidence — наблюдаемый отказ на неподписанном входе.

\n

Эта схема содержит шесть полей, но отвечает на четыре вопроса. Что мы строим — actor, asset и boundary. Что может пойти не так — abuse. Что предпримем — control. Как поймём, что решение достаточно проверено, — evidence. Так модель не смешивает объект защиты, сценарий атаки и способ проверки.

\n
Шесть полей одного потока
ПолеУчебное значениеВопросГраница вывода
Actorbrowser-clientкто формирует вход?не доказывает личность и права пользователя
Assetchange-requestчто нужно сохранить?не заменяет классификацию всех данных
Boundarypublic-api-to-handlerгде меняется доверие к входу?не описывает полную топологию сети
Abuseunsigned requestкакое действие не должно пройти?не является полным списком атак
Controlsigned-requestкакое условие проверяет handler?не доказывает достаточность криптографии
Evidencelocal rejection checkчто можно наблюдать?не является результатом production-проверки или pentest
\n

Что в модели называют риском

\n

Риск здесь — не название уязвимости и не оценка «высокий». Это связка сценария злоупотребления с последствием для актива. Если неподписанный change-request меняет запись, последствием становится потеря целостности этого актива. Контроль должен уменьшать именно такой путь, а evidence — показать, что неподписанный вход действительно не дошёл до записи.

\n

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

\n

Воспроизводимый учебный пример

\n

Ниже — небольшой JavaScript-валидатор записи модели. Он не создаёт ключи, не проверяет подпись HTTP-запроса и не обращается к базе. Его задача уже: не дать пропустить неполное описание потока или контроль, которого нет в согласованном наборе. Запускать пример можно в Node.js или в консоли браузера.

\n
function checkThreatRecord(input = {}) {\n  const required = ['actor', 'asset', 'boundary', 'abuse', 'control', 'evidence'];\n  const missing = required.filter((key) => {\n    return typeof input[key] !== 'string' || input[key].trim() === '';\n  });\n\n  if (missing.length > 0) {\n    return { status: 'reject', reason: 'missing: ' + missing.join(', ') };\n  }\n\n  if (input.control !== 'signed-request') {\n    return { status: 'reject', reason: 'control-not-approved' };\n  }\n\n  return {\n    status: 'accept',\n    asset: input.asset,\n    abuse: input.abuse,\n    control: input.control,\n    evidence: input.evidence\n  };\n}\n\nconst complete = {\n  actor: 'browser-client',\n  asset: 'change-request',\n  boundary: 'public-api-to-handler',\n  abuse: 'unsigned request',\n  control: 'signed-request',\n  evidence: 'local rejection check'\n};\n\nconsole.log(checkThreatRecord(complete).status); // accept\nconsole.log(checkThreatRecord({ ...complete, asset: '' }).reason); // missing: asset\nconsole.log(checkThreatRecord({ ...complete, control: 'rate-limit' }).reason); // control-not-approved
\n

Вызов с полной записью возвращает accept. Пустой asset и неподтверждённый control дают разные причины отказа. Это воспроизводимый результат именно для контракта описания. Он не означает, что реальный handler проверяет подпись, что ключи защищены, что запрос нельзя повторить или что пользователь имеет право изменить запись. Следующий тест должен обращаться к настоящему handler и проверять разрешённый и неподписанный входы отдельно.

\n
\"Схема
Учебная схема связывает change-request, unsigned request, signed-request и local rejection check; она не описывает реальную сеть, ключи или результат проверки endpoint.
\n

Где проверка меняет решение

\n

Полезный поворот расследования происходит не при выборе нового middleware, а при проверке отрицательного пути. Если запись показывает контроль, но не показывает вход, который он должен отвергнуть, команда ещё не связала защиту с угрозой. Если evidence ограничено словом «проверено», другой инженер не сможет повторить вывод.

\n
Симптом, проверка и действие
СимптомПроверкаДействиеЧто ещё не доказано
Есть control, но нет assetкакой объект теряет свойство?назвать один asset до выбора механизмачто контроль защищает весь продукт
Есть abuse, но нет boundaryгде вход меняет статус доверия?показать границу на потокечто другие входы устроены так же
Есть control, но нет evidenceкакой вход должен получить отказ?добавить тест, лог решения или ручную проверкучто реализация уже исправлена
Evidence равно «проверено»какой вход, результат и артефакт?записать три этих значениячто тест покрывает все ветви
Rollback равно «вернуть всё»какие поля и ресурсы изменились?описать границу отката отдельночто удаление контроля безопасно
\n

Порядок проверки модели

\n
  1. Сузьте область. Возьмите один поток, один актив и одну границу. Большая диаграмма не заменяет точное описание выбранного входа.
  2. Опишите злоупотребление действием. «Отправить запрос без действительной подписи» проверяемее, чем слово «подделка».
  3. Назначьте решение. Запишите, в какой точке handler принимает или отклоняет вход. Имя алгоритма не является условием само по себе.
  4. Задайте evidence. Укажите вход, ожидаемый результат и артефакт: unit test, журнал решения или согласованную ручную проверку.
  5. Прогоните отрицательную ветку. Передайте пустой asset, неподписанный вход и неизвестный control. Каждый отказ должен иметь понятную причину.
  6. Сверьте модель с реализацией. Проверьте настоящий handler, конфигурацию, владельца ключей, права и журналирование. Учебный валидатор эти свойства не проверяет.
  7. Опишите откат. Назовите, что возвращается при удалении контроля. Если затрагиваются ключи, миграции, очереди или совместимость, нужен отдельный операционный план.
\n

Ограничения и границы применимости

\n

Минимальная модель не ранжирует риск, не считает вероятность, не строит attack tree и не перечисляет все trust zones. Она не проверяет криптографический протокол, identity provider, права доступа, хранение секретов или журналирование. Для персональных данных, платежей и критичных операций понадобятся классификация, владелец риска и независимая проверка.

\n

Подпись не заменяет авторизацию. Шифрование канала не доказывает целостность бизнес-операции. Rate limit не решает проблему подмены личности. Нельзя переносить учебное signed-request на реальный API без проверки протокола, ключей, времени жизни, повторной отправки и обработки ошибок. Полная запись модели — условие для следующего этапа, а не сертификат безопасности.

\n

Что следует из официальных рамок

\n

OWASP описывает threat modeling как структурированный и повторяемый процесс: сначала моделируют систему, затем выявляют угрозы, выбирают меры и проводят review/validation. В этой рамке отдельно названы потоки данных, хранилища и trust boundaries, поэтому boundary в учебном контракте нельзя заменять общим словом «система».

\n

NIST SP 800-154 связывает data-centric threat modeling с четырьмя шагами: определить систему и интересующие данные, выбрать векторы атаки, охарактеризовать меры защиты и зафиксировать результаты. Это Initial Public Draft 2016 года, а страница NIST сообщает о планах финализации; документ нельзя цитировать как завершённый стандарт. Для конкретного проекта его ограничения и актуальность нужно проверять отдельно.

\n

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

\n

Модель готова к следующему этапу, если независимый инженер без устных пояснений называет actor, asset, boundary, abuse, control и evidence; воспроизводит отказ на неподписанном входе; видит однозначный pass или fail; и понимает, что именно откатывается. Это критерий полноты записи. Для реализации нужен отдельный тест настоящего handler с разрешённым и явно неподписанным входом, а его результат должен принадлежать согласованному артефакту проверки.

\n

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

\n" }