{ "index": 145, "slug": "editorial-2023-12-field-security-audit", "title": "Аудит веб-проекта: как превратить список рисков в безопасный порядок исправлений", "excerpt": "Длинный отчёт аудита не говорит, что исправлять первым. Разбираем учебный кейс через scope, актив, доказательство, влияние и обратимую проверку — с таблицей приоритетов и безопасными командами.", "contentHtml": "

После аудита веб-проекта команда часто получает десятки строк: устаревший пакет, отсутствующий заголовок, подозрительный endpoint, слишком подробную ошибку и возможную ошибку доступа. Такой список описывает наблюдения, но не отвечает на рабочий вопрос: что исправлять сегодня, что проверить дополнительно, а что закрыть как неприменимое. Ошибка в порядке может стоить дороже самой уязвимости: команда потратит окно релиза на косметический заголовок и оставит доступ к чужому документу без проверки.

\n

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

\n

Сначала отделяем наблюдение от риска

\n

Наблюдение отвечает на вопрос «что увидел инструмент или проверяющий». Риск добавляет контекст: какой актив затронут, кто может выполнить действие, какие данные доступны и существует ли рабочий путь к воздействию. Например, заголовок Server с версией раскрывает деталь конфигурации, но сам по себе не доказывает захват сервера. В отличие от этого, воспроизводимый запрос обычного пользователя, который получает чужой документ, уже указывает на нарушение границы доступа.

\n

Полезная запись выглядит как проверяемое утверждение: «роль viewer при запросе к документу другого владельца получает HTTP 200 и тело документа». К ней прикладываются дата и окружение проверки, обезличенный идентификатор, ожидаемый результат и фактический результат. Секреты, персональные данные и полный ответ сессии в отчёт не попадают.

\n
\"Схема
Приоритет появляется после прохождения границ: сначала подтверждаем, что проверяем именно нужный актив, затем связываем находку с владельцем и проверкой результата.
\n

Граница проверки важнее скорости сканера

\n

До активного запроса зафиксируйте scope: домены, окружения, пути, тестовые учётные записи, разрешённые часы и типы нагрузки. Отдельно укажите исключения: платёжные операции, реальные персональные данные, сторонние callback-адреса и любые действия, меняющие состояние. У команды должны быть контакт владельца и способ остановить проверку. Разрешение на тестирование staging не означает разрешение сканировать production или соседний домен.

\n

OWASP WSTG разделяет пассивное изучение и активные тесты, а среди активных категорий отдельно называет авторизацию, сессии, валидацию ввода, бизнес-логику и API. Это полезная карта покрытия, но не обещание, что один прогон обнаружит все дефекты. NIST SP 800-115 также описывает планирование, проведение и анализ тестов и прямо ограничивает документ обзором методов, а не полной программой безопасности.

\n

Для каждой находки добавьте владельца действия. Владелец не обязательно тот, кто нашёл проблему: endpoint может принадлежать команде API, политика cookies — платформе, а решение о временном ограничении доступа — владельцу продукта. Если owner не установлен, строка должна оставаться в очереди уточнения, а не маскироваться высоким баллом.

\n

Строим приоритет из контекста, а не из одного числа

\n

Для первого прохода достаточно пяти полей: ценность актива, достижимость входа, требуемые права, подтверждённость воздействия и обратимость временной меры. Я использую шкалы от 0 до 3 не как стандарт и не как точный расчёт денежного ущерба, а как прозрачное правило очереди. Итоговый балл помогает упорядочить ручную работу; он не заменяет обсуждение владельца и проверку доказательства.

\n
Фактор0–123
Ценность активаТестовые или публичные данныеВнутренние данные или обычный аккаунтПлатёжные, персональные или административные данные
ДостижимостьТолько локально или за несколькими барьерамиДоступно авторизованному пользователюДоступно из публичной точки входа
ПраваНужна привилегированная рольНужна обычная учётная записьДостаточно гостевого запроса
Доказательство воздействияТолько версия, баннер или эвристикаАномальный ответ, но без подтверждения ущербаПовторяемое нарушение инварианта или доступ к тестовым данным
ОбратимостьБезопасный read-only тестНужна изолированная копия или согласованный rollbackИзменяет данные, требует остановки и отдельного разрешения
\n

Сумма не должна скрывать стоп-факторы. Подтверждённый гостевой доступ к персональным данным помещают в срочную очередь даже при низкой уверенности в масштабе. И наоборот, рекомендация обновить библиотеку без версии, затронутого пути и подтверждённой экспозиции сначала требует инвентаризации. Для внешнего сигнала можно добавить наличие CVE в каталоге CISA Known Exploited Vulnerabilities: CISA предлагает использовать этот каталог как вход в собственную модель управления уязвимостями, а не как универсальную замену контексту актива.

\n

Учебный разбор трёх находок

\n

Представим сервис документов на тестовом окружении. Сканер нашёл три проблемы.

\n
  1. Доступ к чужому документу. Пользователь с ролью viewer меняет числовой documentId и получает тело документа другого тестового пользователя. Актив — содержимое документа, вход — публичный API после входа, доказательство — два независимых тестовых аккаунта и повторяемый HTTP 200. Приоритет высокий: сначала ограничить endpoint или отключить спорную операцию, затем исправить проверку владельца и оставить regression-тест.
  2. Отсутствует Content-Security-Policy. Заголовок не найден на HTML-странице. Это полезная защитная мера, но отсутствие CSP не доказывает XSS. Сначала нужно определить, есть ли исполняемые inline-скрипты, доверенные источники и реальный сценарий внедрения. Без такого контекста находка идёт в усиление контроля, а не обгоняет подтверждённую ошибку авторизации.
  3. Устаревшая зависимость. Файл блокировки содержит версию с публичным advisory. Приоритет зависит от того, загружается ли уязвимый код в серверный или клиентский путь, доступен ли затронутый endpoint и есть ли эксплуатация именно этой версии. Исправление начинают с проверки дерева зависимостей и совместимого обновления, а не с безусловной замены пакета в production.
\n

Эти примеры показывают разницу между категорией и решением. OWASP Top 10:2021 удобен для общего языка, но его категория не сообщает владельцу, какой запрос выполнить и какой результат считать исправлением. Для технических требований лучше зафиксировать версию ASVS: идентификаторы требований могут меняться между версиями, поэтому в отчёте рядом с номером нужен тег версии.

\n

Безопасная последовательность воспроизведения

\n

Проверку доступа выполняйте двумя тестовыми аккаунтами, без реальных данных и без методов, меняющих состояние. В примере ниже APP_URL указывает на согласованное тестовое окружение, а TEST_TOKEN — короткоживущий токен пользователя без привилегий. Команды сохраняют только заголовки и тело ответа в локальные временные файлы; подставлять токены в отчёт или историю shell не следует.

\n
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-токен или дополнительный заголовок, включите их только в тестовом контуре и опишите контракт отдельно.

\n

Исправление закрывает инвариант

\n

Хорошая задача на исправление формулируется не как «починить безопасность», а как инвариант. Например: «viewer может читать только документы, перечисленные в его области доступа; запрос к чужому документу возвращает 403 без тела документа; администратор сохраняет разрешённый доступ». Такой контракт связывает код, тест и наблюдение после релиза.

\n

Для ошибки авторизации проверка должна жить рядом с серверным обработчиком, а не только в скрытии кнопки на клиенте. Для заголовка проверьте все HTML-входы, CDN и кэш: один ответ origin с CSP не доказывает, что тот же заголовок дошёл до браузера. Для зависимости зафиксируйте версию lockfile, тесты совместимости и путь отката. OWASP ASVS полезен как список проверяемых технических требований, но не сообщает, какие бизнес-данные критичны именно в вашем проекте.

\n

После изменения повторите исходный сценарий в тех же условиях и выполните соседние негативные проверки. Сохраните старый результат, новый статус, версию сборки и ссылку на тест. Если включена временная блокировка, назначьте срок пересмотра: иначе mitigation легко станет постоянным исключением без владельца.

\n

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

\n

Эта схема рассчитана на веб-приложение и API, где команда может создать тестовые аккаунты, читать журналы запросов и согласовать безопасные GET-проверки. Она не заменяет threat modeling, анализ исходного кода, проверку облачной инфраструктуры, оценку поставщика, юридическое решение о раскрытии или полноценный penetration test. Сканер не видит бизнес-правила, а ручной тест не доказывает отсутствие дефектов в непроверенных ролях и путях.

\n

Не переносите баллы из таблицы между проектами как SLA: шкалы, критичность данных и допустимое время реакции задаёт владелец системы. Не запускайте fuzzing, нагрузку, попытки обхода MFA и тесты удаления на чужом или production-окружении без отдельного письменного scope. Если доказательство требует реальных персональных или платёжных данных, остановите воспроизведение и согласуйте обезличенный fixture.

\n

Критерий готовности очереди

\n

Перед закрытием аудита у каждой существенной строки должны быть актив, затронутый путь, владелец, доказательство, оценка влияния, действие, срок пересмотра и способ проверки результата. В конце проверьте очередь по шагам:

\n
  1. Зафиксировать scope, исключения, роли и контакт для остановки теста.
  2. Удалить дубликаты и отделить эвристику от воспроизводимого нарушения.
  3. Для подтверждённых находок записать инвариант, владельца и обратимую первую меру.
  4. Проверить сначала публичные входы к ценным активам и нарушения границ доступа.
  5. Повторить исходный тест после исправления и добавить его в автоматическую проверку.
  6. Пересмотреть остаточные риски и явно оставить в очереди то, что не удалось проверить.
\n

Так длинный отчёт превращается в управляемую последовательность: сначала защищаем актив с доказанным воздействием, затем закрываем повторяемый путь, после чего усиливаем контроли и покрытие. Аудит заканчивается не красивым сканером, а результатом, который другой инженер может воспроизвести и проверить.

\n

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

" }