Руководства по безопасности

Как по-настоящему закрывать CVE в зависимостях: сканировать, исправлять, изолировать и продолжать следить

Как по-настоящему закрывать уязвимости в зависимостях: скан, исправление, изоляция и мониторинг, чтобы исправление не исчезло при следующем деплое.

Опубликовано 2026-06-11 Обновлено 2026-10-05 8 мин чтения
На этой странице

Для тех, кто запускает несколько веб-приложений (разные фреймворки) малой командой и хочет исправить уязвимости зависимостей (CVE) по-настоящему, раз и навсегда. Без шагов атаки — только практика исправления, закрепления исправлений и наблюдения. Про тип инцидента, который часто это запускает, см. заброшенная публичная RCE и мошеннические счета.

Взгляд этого сайта: малые команды остаются в безопасности за счёт двух дисциплин

В малой команде работает не яркий инструментарий, а две дисциплины: 1) автоматическое обнаружение изменений (оповещать только о новых уязвимостях) и 2) локально→push→деплой (продакшен принимает, никогда не правит). Этот сайт работает так же: аудит зависимостей (pnpm audit) перед каждым деплоем плюс ежедневный cron, с push-to-deploy, доставляющим в продакшен. Настроить оповещения, которые продолжают работать, вместо разового исправления — самый дешёвый подход, который держится долго.

Сначала определите четыре условия «готовности»

Это не даёт объявить «готово» слишком рано. Решите заранее: пока не сойдутся все четыре, это незавершённо.

1. Скан

перевести состояние в цифры

2. Фикс

исправление не-dev крит/высоких в источнике

3. Изоляция/передача

сделать неисправимое явным

4. Мониторинг

наладить ежедневное обнаружение изменений

Работа с уязвимостями готова = набор из четырёх. Пропустите одно — и уязвимость останется, хотя вы считаете, что исправили её.

1. Скан: посчитайте, что есть сейчас

Сначала увидьте, что и где, машинно. Ручные разовые проверки всегда срываются.

1

Автоматически находите lock-файлы и сканируйте

Найдите composer.lock / package-lock.json / pnpm-lock.yaml и прогоняйте их ежедневно через сканер с открытым кодом (osv-scanner или pnpm audit). Он лишь читает lock-файлы, так что нагрузка крошечная.
2

Знайте, что серьёзность — не одно число

Один и тот же изъян может быть High по CVSS (скажем, 7.5), но Moderate по выверенной оценке вендора. CVSS вычисляется машинно и склонен завышать. Решите, какая основа задаёт ваш порог, и убедитесь, что вся команда знает, какая в ходу (→ что такое CVSS). Скрестите с тем, эксплуатируется ли он реально (KEV), для приоритизации.
3

Исключайте шум С записанной причиной, а не игнорированием

Dev-зависимости только для тестов/сборки часто не попадают в продакшен-бандл и могут не нести реального риска. Используйте группировку сканера, чтобы убрать dev из уведомлений, но записывайте, почему вы исключили. Сохраните способность ответить «а это разве всё ещё не исправлено?» позже.

2. Фикс: обновите саму уязвимую версию

Временная мера, скрывающая симптом, и устранение настоящей причины — разные задачи. Сделайте обе, чтобы закончить.

Только сокрытие симптома (причина остаётся)

  • блокировать только «подозрительно выглядящие» запросы на обратном прокси
  • симптом прекратился, значит назвать «разобрались»
  • уязвимая зависимость остаётся — RCE всё ещё можно эксплуатировать

Устранение причины (правильно)

  • обновить уязвимую зависимость до исправленной версии
  • после обновления убедиться, что сигнатура исчезла из логов
  • считать временную меру и настоящее исправление двумя разными задачами и делать обе

Проверяйте мажорные обновления сборкой ДО продакшена

В отличие от патча, скачок мажорной версии (фреймворк 14→15, UI-кит 4→6 и т. п.) несёт ломающие изменения. Обновление без проверки, ломающее прод-сборку, роняет сервис. Поместите изменённый исходник в идентичную проду среду выполнения (контейнер с той же версией Node/PHP) и добейтесь полностью зелёной сборки/проверки типов до push. Устраняйте ошибки по одной (codemod sync→async, многострочные строки CSS-классов, упразднённое глобальное пространство типов и т. п.). Если старый контейнер продолжает работать, при сбое откат проходит без простоя.

Убедитесь, что следующий деплой не отменит исправление

Идеальное исправление стоит ноль, если следующий деплой его стирает. Это самая частая ошибка.

1

Не коммитьте в продакшен-дереве

Прямой коммит в продакшене разветвляет историю от центрального репозитория, и следующий деплой (pull / checkout -f) перезаписывает его, и исправление исчезает. Продакшен — это «куда приземляются результаты деплоя», а не где вы правите.
2

Стандартизируйте одно направление: локально→push→деплой

Правьте локально → коммит → push → (продакшен) делает pull / автодеплой. Даже изменение, которое пришлось проверять на продакшене, нужно отмотать на локаль и заново запушить (→ сделать продакшен только на приём).
3

Не доверяйте HTTP 200 как «здоровью»

На шаред-хостинге фатальная ошибка всё равно может вернуть 200. Проверяйте по реальному содержимому — отрисован ли ожидаемый заголовок/текст (curl | grep), нет ли новых ошибок в логах, и пройдите по динамическим маршрутам (на БД, с параметрами), а не только по главной. Кэши могут задержать изменение, так что подождите или сначала очистите.

3. Изоляция/передача: запишите то, что не можете исправить

Не всегда можно исправить всё сейчас. Хитрость — никогда не оставлять «не могу исправить / не моя зона / не используется» неопределённым.

1

Осиротевший/EOL код: изолируйте ПЕРЕД удалением

Старый исходник, который вы больше не обслуживаете (EOL-фреймворки и т. п.), — не удаляйте напрямую; переименуйте для изоляции и оставьте пометку, когда/почему и где он обслуживался. Уберите его и из сканов. «Удалить» необратимо; «изолировать» обратимо и сохраняет след.
2

Сначала убедитесь, что на него реально нет ссылок

Проследите конфигурацию обратного прокси, настройку сборки и монтирования, чтобы убедиться, что ничто на него не ссылается, прежде чем изолировать, — так вы не начнёте случайно снова его обслуживать.
3

Явно передавайте то, что не можете исправить

То, что вне вашей досягаемости, передавайте с указанием «кто / что», а не оставляйте. Ценность инвентаризации — не дать неисправленному превратиться в невидимую запущенность.

4. Мониторинг: наладьте ежедневное обнаружение изменений

Теперь, наконец, поставьте механизм оповещения. Без него сделанное исправление не сообщит вам, когда оно повторится.

Оповещайте о НОВОМ, а не обо всём каждый день

Сравнивайте результаты скана с предыдущим прогоном (файлом состояния) и уведомляйте только при появлении нового критического/высокого. Одно и то же содержимое каждый день начинают игнорировать (усталость от оповещений). Сводите в одно письмо (новые / устранённые / текущие) по ежедневному cron. Даже шаред-хостинг нормально тянет один бинарник сканера + cron (нагрузка — миллисекунды; добавьте nice для спокойствия).

diff
уведомлять только о новом = без усталости
ежедневно
держите запущенным на cron
1 письмо
сводка новые / устранённые / текущие
все 4
готово только когда включён мониторинг

API-ключи и SSH-ключи, которые инвентаризация тоже находит

Реальное закрытие CVE часто выявляет и проблемы, не связанные с зависимостями. Две частые — файл секрета, оставленный в публичной директории (старый токен в веб-корне; если он из общего шаблона, у каждого сайта та же проблема) и корневой ключ, выданный среде, которую можно скомпрометировать. Оба создают паттерн «одна утечка — всё», так что проверяйте их в том же проходе (каждое заслуживает отдельного разбора). По основам секретов см. безопасное хранение секретов и базовый чек-лист.

Читать дальше

FAQ

QКогда работа с уязвимостями действительно «завершена»?
A

Только когда сходятся четыре вещи: 1) вы просканировали и перевели состояние в цифры, 2) вы исправили не-dev критические/высокие, 3) вы явно изолировали или передали то, что не можете исправить / осиротевший код, и 4) вы наладили ежедневное обнаружение изменений (мониторинг, ловящий новые и повторяющиеся проблемы). Пока нет п. 4, это не готово — зависимости снова станут уязвимыми завтра.

QНе вызовет ли ежедневное сканирование усталость от оповещений?
A

Оповещение обо всём ежедневно быстро начинают игнорировать. Сравнивайте с предыдущим результатом (файлом состояния) и уведомляйте только при появлении НОВОГО критического/высокого — обнаружение изменений. Не отправлять одно и то же каждый день — вот что делает мониторинг по-настоящему долговечным.

QПочему исправление иногда откатывается к уязвимому?
A

Коммит прямо в продакшен-дереве означает, что следующий деплой (pull или checkout -f) перезаписывает и стирает исправление. Стандартизируйте одно направление — локально→push→деплой — и сделайте продакшен «только на приём». Идеальное исправление в процессе, который его стирает, стоит ноль.