{ "index": 145, "slug": "editorial-2023-12-field-security-audit", "title": "Аудит веб-проекта: как превратить список рисков в безопасный порядок исправлений", "excerpt": "Длинный отчёт аудита не говорит, что исправлять первым. Разбираем учебный кейс через scope, актив, доказательство, влияние и обратимую проверку — с таблицей приоритетов и безопасными командами.", "contentHtml": "
После аудита веб-проекта команда часто получает десятки строк: устаревший пакет, отсутствующий заголовок, подозрительный endpoint, слишком подробную ошибку и возможную ошибку доступа. Такой список описывает наблюдения, но не отвечает на рабочий вопрос: что исправлять сегодня, что проверить дополнительно, а что закрыть как неприменимое. Ошибка в порядке может стоить дороже самой уязвимости: команда потратит окно релиза на косметический заголовок и оставит доступ к чужому документу без проверки.
\nНиже — учебный кейс, а не отчёт о конкретной боевой системе. Цель — получить воспроизводимый способ сортировки находок. Для каждой строки нужны четыре вещи: затронутый актив, доказательство, возможное воздействие и следующий безопасный шаг. Без этой связки уровень в сканере остаётся гипотезой, а не основанием для изменения.
\nНаблюдение отвечает на вопрос «что увидел инструмент или проверяющий». Риск добавляет контекст: какой актив затронут, кто может выполнить действие, какие данные доступны и существует ли рабочий путь к воздействию. Например, заголовок Server с версией раскрывает деталь конфигурации, но сам по себе не доказывает захват сервера. В отличие от этого, воспроизводимый запрос обычного пользователя, который получает чужой документ, уже указывает на нарушение границы доступа.
Полезная запись выглядит как проверяемое утверждение: «роль viewer при запросе к документу другого владельца получает HTTP 200 и тело документа». К ней прикладываются дата и окружение проверки, обезличенный идентификатор, ожидаемый результат и фактический результат. Секреты, персональные данные и полный ответ сессии в отчёт не попадают.
До активного запроса зафиксируйте scope: домены, окружения, пути, тестовые учётные записи, разрешённые часы и типы нагрузки. Отдельно укажите исключения: платёжные операции, реальные персональные данные, сторонние callback-адреса и любые действия, меняющие состояние. У команды должны быть контакт владельца и способ остановить проверку. Разрешение на тестирование staging не означает разрешение сканировать production или соседний домен.
\nOWASP WSTG разделяет пассивное изучение и активные тесты, а среди активных категорий отдельно называет авторизацию, сессии, валидацию ввода, бизнес-логику и API. Это полезная карта покрытия, но не обещание, что один прогон обнаружит все дефекты. NIST SP 800-115 также описывает планирование, проведение и анализ тестов и прямо ограничивает документ обзором методов, а не полной программой безопасности.
\nДля каждой находки добавьте владельца действия. Владелец не обязательно тот, кто нашёл проблему: endpoint может принадлежать команде API, политика cookies — платформе, а решение о временном ограничении доступа — владельцу продукта. Если owner не установлен, строка должна оставаться в очереди уточнения, а не маскироваться высоким баллом.
\nДля первого прохода достаточно пяти полей: ценность актива, достижимость входа, требуемые права, подтверждённость воздействия и обратимость временной меры. Я использую шкалы от 0 до 3 не как стандарт и не как точный расчёт денежного ущерба, а как прозрачное правило очереди. Итоговый балл помогает упорядочить ручную работу; он не заменяет обсуждение владельца и проверку доказательства.
\n| Фактор | 0–1 | 2 | 3 |
|---|---|---|---|
| Ценность актива | Тестовые или публичные данные | Внутренние данные или обычный аккаунт | Платёжные, персональные или административные данные |
| Достижимость | Только локально или за несколькими барьерами | Доступно авторизованному пользователю | Доступно из публичной точки входа |
| Права | Нужна привилегированная роль | Нужна обычная учётная запись | Достаточно гостевого запроса |
| Доказательство воздействия | Только версия, баннер или эвристика | Аномальный ответ, но без подтверждения ущерба | Повторяемое нарушение инварианта или доступ к тестовым данным |
| Обратимость | Безопасный read-only тест | Нужна изолированная копия или согласованный rollback | Изменяет данные, требует остановки и отдельного разрешения |
Сумма не должна скрывать стоп-факторы. Подтверждённый гостевой доступ к персональным данным помещают в срочную очередь даже при низкой уверенности в масштабе. И наоборот, рекомендация обновить библиотеку без версии, затронутого пути и подтверждённой экспозиции сначала требует инвентаризации. Для внешнего сигнала можно добавить наличие CVE в каталоге CISA Known Exploited Vulnerabilities: CISA предлагает использовать этот каталог как вход в собственную модель управления уязвимостями, а не как универсальную замену контексту актива.
\nПредставим сервис документов на тестовом окружении. Сканер нашёл три проблемы.
\nviewer меняет числовой documentId и получает тело документа другого тестового пользователя. Актив — содержимое документа, вход — публичный API после входа, доказательство — два независимых тестовых аккаунта и повторяемый HTTP 200. Приоритет высокий: сначала ограничить endpoint или отключить спорную операцию, затем исправить проверку владельца и оставить regression-тест.Content-Security-Policy. Заголовок не найден на HTML-странице. Это полезная защитная мера, но отсутствие CSP не доказывает XSS. Сначала нужно определить, есть ли исполняемые inline-скрипты, доверенные источники и реальный сценарий внедрения. Без такого контекста находка идёт в усиление контроля, а не обгоняет подтверждённую ошибку авторизации.Эти примеры показывают разницу между категорией и решением. OWASP Top 10:2021 удобен для общего языка, но его категория не сообщает владельцу, какой запрос выполнить и какой результат считать исправлением. Для технических требований лучше зафиксировать версию ASVS: идентификаторы требований могут меняться между версиями, поэтому в отчёте рядом с номером нужен тег версии.
\nПроверку доступа выполняйте двумя тестовыми аккаунтами, без реальных данных и без методов, меняющих состояние. В примере ниже APP_URL указывает на согласованное тестовое окружение, а TEST_TOKEN — короткоживущий токен пользователя без привилегий. Команды сохраняют только заголовки и тело ответа в локальные временные файлы; подставлять токены в отчёт или историю shell не следует.
export APP_URL='https://staging.example.test'\nexport TEST_TOKEN='replace-with-short-lived-viewer-token'\n\n# 1. Проверяем ожидаемый публичный ответ, не меняя состояние.\ncurl -sS -D /tmp/audit-public.headers -o /tmp/audit-public.body \\\n --max-time 5 -w 'public status=%{http_code}\\n' \\\n \"$APP_URL/api/documents/42\"\n\n# 2. Повторяем тот же GET от имени viewer.\ncurl -sS -D /tmp/audit-viewer.headers -o /tmp/audit-viewer.body \\\n --max-time 5 -H \"Authorization: Bearer $TEST_TOKEN\" \\\n -w 'viewer status=%{http_code}\\n' \\\n \"$APP_URL/api/documents/42\"\n\n# 3. Сверяем только статус и безопасный признак тела.\ndiff -u /tmp/audit-public.headers /tmp/audit-viewer.headers || true\nsha256sum /tmp/audit-public.body /tmp/audit-viewer.body\nКоманды дают сигнал, но не доказывают именно IDOR (доступ к объекту по изменяемому идентификатору), пока 42 не принадлежит другой тестовой учётной записи и тело не содержит её документ. Для доказательства добавьте в fixture два заранее созданных объекта с известными владельцами, проверьте ожидаемый 403 или безопасный 404 и убедитесь, что ответ не раскрывает содержимое. Если endpoint использует cookie, CSRF-токен или дополнительный заголовок, включите их только в тестовом контуре и опишите контракт отдельно.
Хорошая задача на исправление формулируется не как «починить безопасность», а как инвариант. Например: «viewer может читать только документы, перечисленные в его области доступа; запрос к чужому документу возвращает 403 без тела документа; администратор сохраняет разрешённый доступ». Такой контракт связывает код, тест и наблюдение после релиза.
\nДля ошибки авторизации проверка должна жить рядом с серверным обработчиком, а не только в скрытии кнопки на клиенте. Для заголовка проверьте все HTML-входы, CDN и кэш: один ответ origin с CSP не доказывает, что тот же заголовок дошёл до браузера. Для зависимости зафиксируйте версию lockfile, тесты совместимости и путь отката. OWASP ASVS полезен как список проверяемых технических требований, но не сообщает, какие бизнес-данные критичны именно в вашем проекте.
\nПосле изменения повторите исходный сценарий в тех же условиях и выполните соседние негативные проверки. Сохраните старый результат, новый статус, версию сборки и ссылку на тест. Если включена временная блокировка, назначьте срок пересмотра: иначе mitigation легко станет постоянным исключением без владельца.
\nЭта схема рассчитана на веб-приложение и API, где команда может создать тестовые аккаунты, читать журналы запросов и согласовать безопасные GET-проверки. Она не заменяет threat modeling, анализ исходного кода, проверку облачной инфраструктуры, оценку поставщика, юридическое решение о раскрытии или полноценный penetration test. Сканер не видит бизнес-правила, а ручной тест не доказывает отсутствие дефектов в непроверенных ролях и путях.
\nНе переносите баллы из таблицы между проектами как SLA: шкалы, критичность данных и допустимое время реакции задаёт владелец системы. Не запускайте fuzzing, нагрузку, попытки обхода MFA и тесты удаления на чужом или production-окружении без отдельного письменного scope. Если доказательство требует реальных персональных или платёжных данных, остановите воспроизведение и согласуйте обезличенный fixture.
\nПеред закрытием аудита у каждой существенной строки должны быть актив, затронутый путь, владелец, доказательство, оценка влияния, действие, срок пересмотра и способ проверки результата. В конце проверьте очередь по шагам:
\nТак длинный отчёт превращается в управляемую последовательность: сначала защищаем актив с доказанным воздействием, затем закрываем повторяемый путь, после чего усиливаем контроли и покрытие. Аудит заканчивается не красивым сканером, а результатом, который другой инженер может воспроизвести и проверить.
\n