Files

8 lines
24 KiB
JSON
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"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>"
}