8 lines
24 KiB
JSON
8 lines
24 KiB
JSON
{
|
||
"index": 147,
|
||
"slug": "editorial-2023-12-practice-security-audit",
|
||
"title": "Аудит веб-проекта: scope, разрешение и воспроизводимая проверка",
|
||
"excerpt": "Как очертить путь данных, разделить scope и разрешение и подготовить одну безопасную проверку до активного тестирования. С картой границ, локальным fail-closed примером и диагностической таблицей.",
|
||
"contentHtml": "<p>Проблема аудита веб-проекта обычно появляется ещё до первого запроса. В задаче есть домен и слово «проверить», но не указано, входит ли в работу identity provider, CDN, файловое хранилище и тестовая база. Один инженер начинает с формы входа, другой — с заголовков прокси. В конце получается длинный отчёт без ответа на главный вопрос: какую систему и с каким разрешением действительно проверили.</p>\n<p>Цена ошибки двойная. Пропущенный участок остаётся без владельца. Лишний запрос может попасть в контур подрядчика, изменить данные или затронуть настоящих пользователей. Поэтому безопасный аудит начинается не со сканера, а с короткого контракта: пользовательский путь, активы, границы, разрешённые действия и проверяемое утверждение.</p>\n<p><strong>Тезис.</strong> Сначала нужно сделать результат ограниченным, а уже потом подробным. Если другой инженер может показать тот же путь, назвать владельца каждого перехода и повторить разрешённую проверку на учебных данных, scope готов к работе. Если хотя бы одно поле неизвестно, это пробел в покрытии, а не доказательство низкого риска.</p>\n<h2>Сначала карта пути, потом список URL</h2>\n<p>URL обозначает точку входа, но не весь поток данных. При загрузке документа браузер отправляет байты в API, API проверяет сессию и тип файла, сервис хранения назначает ключ, а отдельный endpoint позже решает, можно ли этот ключ выдать. Если записать только URL загрузки, право чтения останется за пределами карты.</p>\n<p>Для первого прохода возьмите один путь с понятной ценой ошибки: изменение профиля, платёж или получение документа. Идите по нему от действия пользователя до конечного эффекта. На каждом переходе задайте четыре вопроса: какой объект передаётся, кто принимает решение, кто владеет участком и какое утверждение можно проверить без расширения scope.</p>\n<table><caption>Минимальная карточка участка аудита</caption><thead><tr><th scope=\"col\">Поле</th><th scope=\"col\">Учебное значение</th><th scope=\"col\">Что проверяем</th><th scope=\"col\">Граница вывода</th></tr></thead><tbody><tr><td>Актив</td><td><code>profile-api</code></td><td>API участвует в выбранном пути</td><td>Не доказывает, что API доступно из production</td></tr><tr><td>Переход</td><td>Браузер → API</td><td>Где запрос меняет владельца решения</td><td>Не доказывает наличие защиты на переходе</td></tr><tr><td>Владелец</td><td>Команда профиля</td><td>Кому подтвердить контракт и остановку</td><td>Не означает разрешение на любой метод</td></tr><tr><td>Данные</td><td>Идентификатор пользователя</td><td>Какие последствия у ошибочного чтения</td><td>Не доказывает место хранения поля</td></tr><tr><td>Утверждение</td><td>reader не читает чужой профиль</td><td>Субъект, ресурс и ожидаемый отказ</td><td>Не является найденной уязвимостью</td></tr></tbody></table>\n<figure><img src=\"/assets/editorial/2023/security-audit-2023-scope-map.svg\" alt=\"Учебная карта аудита: браузер, веб-API, identity provider и выдача файла соединены границами, у каждого участка указан владелец и вопрос проверки\" loading=\"lazy\" /><figcaption>Карта показывает не сеть, а способ разговора об ответственности. В ней нет адресов, секретов и разрешения на активное тестирование.</figcaption></figure>\n<p>Карта должна быть графом переходов, а не перечнем компонентов. Для каждого перехода зафиксируйте вход, выход и решение. Например, «API получил идентификатор файла» — это факт о входе. «API проверил владельца до чтения» — проверяемое утверждение. «Файл нельзя получить» — слишком сильный вывод, пока не указаны роль, ресурс, среда и способ проверки.</p>\n<h2>Scope и разрешение — разные записи</h2>\n<p><em>Scope</em> отвечает на вопрос «что обсуждаем»: домен, приложение, среда, endpoint, хранилище, учётные записи и путь пользователя. <em>Authorization</em> отвечает на вопрос «что можно делать»: метод, период, лимит запросов, тестовые данные, запретные действия, контакт для остановки и допустимый результат.</p>\n<p>Публичность не равна разрешению. Доступная форма не разрешает менять чужой идентификатор. Документация API не разрешает отправлять нагрузку. Запись <code>security.txt</code> задаёт канал для сообщений о проблемах; RFC 9116 отдельно предупреждает, что наличие или отсутствие файла не следует трактовать как разрешение или запрет тестирования.</p>\n<p>Эти записи полезно разделять даже внутри одной задачи. Scope может быть согласован для production, а активные запросы — только для staging. Владелец может разрешить чтение с двумя учебными аккаунтами, но не изменение данных. Наблюдение может быть воспроизводимым, а причина — ещё гипотезой. Одна строка «аудит разрешён» стирает эти различия.</p>\n<table><caption>Что должно быть подтверждено до действия</caption><thead><tr><th scope=\"col\">Слой</th><th scope=\"col\">Минимальный вопрос</th><th scope=\"col\">Если ответа нет</th></tr></thead><tbody><tr><td>Объект</td><td>Какой домен, среда и путь входят в работу?</td><td>Остановиться на инвентаризации</td></tr><tr><td>Владелец</td><td>Кто отвечает за актив и принимает результат?</td><td>Назначить контакт до теста</td></tr><tr><td>Действие</td><td>Какой метод разрешён: чтение, запись или только анализ?</td><td>Не расширять метод по ходу</td></tr><tr><td>Ограничение</td><td>Какие аккаунты, данные, лимит и стоп-условие действуют?</td><td>Сформулировать явное «не проверяем»</td></tr></tbody></table>\n<h2>Превращаем тему в проверяемое утверждение</h2>\n<p>Фраза «проверить авторизацию» не задаёт воспроизводимость. Утверждение «учебный пользователь с ролью reader получает отказ при чтении профиля второго учебного пользователя по изменённому идентификатору» уже содержит субъект, ресурс, действие и ожидаемый результат. Его можно проверить одним сценарием и не выдавать этот сценарий за покрытие всех ролей.</p>\n<p>Ниже — самодостаточный пример на Node.js. Он не открывает URL, не сканирует сеть и не имитирует разрешение. Запустите его на Node.js 18 или новее: сохраните блок как <code>audit-boundary.mjs</code> и выполните командой <code>node audit-boundary.mjs</code>. В рабочей системе значения владельца, среды и согласования должны прийти из вашего процесса, а не из этого примера.</p>\n<pre><code>const boundary = {\n asset: 'profile-api',\n environment: 'staging',\n owner: 'profile-team',\n subject: 'reader-test-account',\n resource: 'second-test-profile',\n method: 'read-only-two-account-check',\n permission: 'approved-by-owner',\n rateLimit: '1 request',\n stopContact: 'profile-on-call',\n};\n\nconst required = [\n 'asset', 'environment', 'owner', 'subject', 'resource',\n 'method', 'permission', 'rateLimit', 'stopContact',\n];\n\nfor (const field of required) {\n if (!boundary[field]) {\n throw new Error('incomplete audit boundary: ' + field);\n }\n}\n\nif (boundary.permission !== 'approved-by-owner') {\n throw new Error('active testing is not authorized');\n}\n\nif (boundary.method !== 'read-only-two-account-check') {\n throw new Error('method is outside the example scope');\n}\n\nconsole.log('PASS: boundary is ready for one read-only check');</code></pre>\n<p>Ожидаемый вывод — одна строка <code>PASS</code>. Если удалить <code>permission</code> или заменить метод на запись, скрипт завершится ошибкой до обращения к системе. Это полезный fail-closed барьер, но не контроль доступа: он не видит письмо владельца, не проверяет права в API и не доказывает, что сервер вернёт <code>403</code>.</p>\n<h2>Разделяем наблюдение, гипотезу и вывод</h2>\n<p>У каждого результата должна быть короткая цепочка: симптом → причина → проверка → действие. Симптом — то, что можно повторить. Причина — объяснение, которое ещё нужно подтвердить. Проверка — минимальный эксперимент в разрешённой среде. Действие — изменение или следующий безопасный шаг, а не автоматическая реакция на слово «критический».</p>\n<table><caption>Диагностика незрелого аудита</caption><thead><tr><th scope=\"col\">Симптом</th><th scope=\"col\">Рабочая гипотеза</th><th scope=\"col\">Проверка</th><th scope=\"col\">Действие</th></tr></thead><tbody><tr><td>В задаче указан только домен</td><td>Точку входа приняли за систему</td><td>Пройти путь до identity provider, API и хранения</td><td>Добавить узлы, переходы и владельцев</td></tr><tr><td>Сканер дал много предупреждений</td><td>Нет вопроса и приоритета</td><td>Для сигнала записать вход, ответ, актив и версию среды</td><td>Отделить кандидат от подтверждённого факта</td></tr><tr><td>Проверяющий не знает, где остановиться</td><td>Не заданы лимит и стоп-контакт</td><td>Сверить метод с разрешением владельца</td><td>Не начинать активное действие</td></tr><tr><td>После входа доступен файл</td><td>Проверили экран, но не выдачу</td><td>Проверить право чтения отдельным учебным аккаунтом</td><td>Добавить выдачу в карту и вопрос</td></tr><tr><td>Один запрос вернул <code>403</code></td><td>Отказ принимают за полное покрытие</td><td>Повторить с указанным субъектом и ресурсом</td><td>Описать границу результата, не обещать «всё закрыто»</td></tr></tbody></table>\n<p>В отчёте храните входные условия и ответ, но не прикладывайте секреты и лишние персональные данные. Для HTTP-проверки это обычно метод, нормализованный путь без токена, код ответа, существенные заголовки, время, версия среды и идентификатор тестовой учётной записи. Такой набор позволяет повторить наблюдение и не превращает отчёт в новый источник утечки.</p>\n<h2>Отрицательный путь — часть результата</h2>\n<p>Хорошая карта описывает, когда инженер обязан остановиться. Актив найден, но владелец неизвестен — записываем пробел и не отправляем запрос. Разрешение относится к staging — production исключаем. Разрешён один запрос чтения — не добавляем перебор идентификаторов и не проверяем запись. Это не отказ от аудита, а контроль радиуса действия.</p>\n<p>Технический отказ тоже нужно называть точно. Ответ <code>403</code> на один запрос подтверждает отказ для конкретного субъекта, ресурса, метода и контекста. Он не доказывает, что тот же объект не выдаётся через экспорт, кеш, другой endpoint или другой слой прокси. Если эти ветки не проверялись, пишите «не проверено», а не «отсутствует».</p>\n<p>OWASP WSTG v4.2 предлагает сначала собрать сведения и карту архитектуры, а затем связывать её с тестами. Это помогает не потерять reverse proxy, сервер приложения и внешнюю службу идентификации. Но методика не заменяет согласование доступа: она описывает подход к тестированию, а не выдаёт право тестировать конкретный домен.</p>\n<h2>Порядок работы, который можно повторить</h2>\n<ol><li>Выберите один пользовательский путь и запишите его конечный эффект.</li><li>Нарисуйте переходы: браузер, прокси, API, identity provider, очередь, хранилище и выдача — только если они участвуют в пути.</li><li>Для каждого участка укажите актив, владельца, класс данных, вход, выход и проверяемое утверждение.</li><li>Отдельно зафиксируйте среду, учебные аккаунты, метод, лимит, окно времени, запретные действия и контакт остановки.</li><li>Сформулируйте положительный и отрицательный сценарии. Первый показывает разрешённый доступ, второй — ожидаемый отказ.</li><li>Запустите сначала проверку контракта локально, как в примере. Только после этого выполняйте согласованный запрос.</li><li>Сохраните минимальный evidence: условия, запрос, ответ, время, версию среды и границу результата.</li><li>Разделите запись на факт, гипотезу, ограничение и следующее действие. Владельца исправления назначайте после подтверждения симптома.</li><li>Перед следующим активом или новым методом повторите проверку scope и разрешения.</li></ol>\n<h2>Ограничения применимости</h2>\n<p>Карта активов решает узкую задачу: не потерять границы и не начать активное действие без понятного контракта. Она не заменяет threat model, анализ исходного кода, тестирование инфраструктуры, privacy review, внешний penetration test или план реагирования.</p>\n<p>Staging не доказывает поведение production. Два учебных аккаунта не покрывают все роли и tenant-связи. Локальный fail-closed скрипт не контролирует сервер. Сканер помогает находить кандидатов, но предупреждение не является finding до проверки входа, ответа, воспроизводимости и влияния.</p>\n<p>Нельзя переносить разрешение с одного домена на соседний. Нельзя объявлять покрытие по числу URL. Нельзя обещать отсутствие уязвимостей по одному маршруту. Если архитектура меняется быстрее документации, оставьте поле неизвестным, назначьте владельца и ограничьте действие до безопасной инвентаризации.</p>\n<h2>Критерий готовности к первой проверке</h2>\n<p>Scope готов, когда другой инженер без устного пояснения может показать выбранный путь, активы и переходы, владельца каждого участка, данные и границы ответственности. Он также называет разрешённую среду, аккаунты, метод, лимит, окно времени, стоп-контакт, запрос и ожидаемый отрицательный ответ.</p>\n<p>Проверить готовность можно на одном учебном сценарии. Если команда не может объяснить, почему конкретный запрос разрешён, какой результат считается отказом и что произойдёт при отклонении от метода, активное тестирование не начинается. Следующий шаг — закрыть один конкретный пробел в карте. После этого критерий проверяется заново.</p>\n<h2>Проверяемые источники</h2>\n<ul><li><a href=\"https://csrc.nist.gov/pubs/sp/800/115/final\" target=\"_blank\" rel=\"noopener noreferrer\">NIST SP 800-115: Technical Guide to Information Security Testing and Assessment</a> — официальный обзор планирования, проведения, анализа и смягчения последствий технических проверок. Документ опубликован в 2008 году и не является готовым регламентом для конкретной организации.</li><li><a href=\"https://owasp.org/www-project-web-security-testing-guide/v42/4-Web_Application_Security_Testing/01-Information_Gathering/10-Map_Application_Architecture\" target=\"_blank\" rel=\"noopener noreferrer\">OWASP Web Security Testing Guide v4.2: Map Application Architecture</a> — версия руководства от 2020 года о построении карты компонентов и сетевых зон перед детальным тестированием. Она не создаёт разрешение и не доказывает покрытие.</li><li><a href=\"https://github.com/OWASP/ASVS/releases/tag/v4.0.3\" target=\"_blank\" rel=\"noopener noreferrer\">OWASP ASVS 4.0.3</a> — официальный стабильный релиз требований к техническим контролям веб-приложения от октября 2021 года. Требования помогают формулировать проверяемые условия, но не заменяют scope, evidence и authorization.</li><li><a href=\"https://www.rfc-editor.org/rfc/rfc9116.html\" target=\"_blank\" rel=\"noopener noreferrer\">RFC 9116: A File Format to Aid in Security Vulnerability Disclosure</a> — стандарт IETF от апреля 2022 года о формате <code>security.txt</code>. Раздел 5.5 прямо отделяет disclosure-канал от разрешения на тестирование; область файла также привязана к домену URI, с которого он получен.</li></ul>"
|
||
}
|