{ "index": 179, "slug": "editorial-2023-01-mechanism-threat-model", "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" }