Руководства по безопасности
Как по-настоящему закрывать CVE в зависимостях: сканировать, исправлять, изолировать и продолжать следить
Плейбук того, как реально закрывать уязвимости (CVE) в зависимостях: определение готовности из 4 частей — скан, фикс, изоляция, мониторинг — и уроки из практики, чтобы исправления не исчезали при следующем деплое (локально → push → деплой).
Для тех, кто запускает несколько веб-приложений (разные фреймворки) малой командой и хочет исправить уязвимости зависимостей (CVE) по-настоящему, раз и навсегда. Без шагов атаки — только практика исправления, закрепления исправлений и наблюдения. Про тип инцидента, который часто это запускает, см. заброшенная публичная RCE и мошеннические счета.
Взгляд этого сайта: малые команды остаются в безопасности за счёт двух дисциплин
В малой команде работает не яркий инструментарий, а две дисциплины: 1) автоматическое обнаружение изменений (оповещать только о новых уязвимостях) и 2) локально→push→деплой (продакшен принимает, никогда не правит). Этот сайт работает так же: аудит зависимостей (pnpm audit) перед каждым деплоем плюс ежедневный cron, с push-to-deploy, доставляющим в продакшен. Относиться к безопасности как к «системе, которая продолжает срабатывать», а не как к «разовому исправлению» — самое дешёвое из того, что держится.
Сначала зафиксируйте «определение готовности» из 4 частей
Защита от преждевременного объявления «готово». Решите заранее: пока не сойдутся все четыре, это незавершённо.
1. Скан
перевести состояние в цифры
2. Фикс
корневое исправление не-dev крит/высоких
3. Изоляция/передача
сделать неисправимое явным
4. Мониторинг
наладить ежедневное обнаружение изменений
1. Скан: переведите состояние в цифры
Сначала увидьте, что и где, машинно. Ручные разовые проверки всегда срываются.
Автоматически находите lock-файлы и сканируйте
composer.lock / package-lock.json / pnpm-lock.yaml и прогоняйте их ежедневно через сканер с открытым кодом (osv-scanner или pnpm audit). Он лишь читает lock-файлы, так что нагрузка крошечная.Знайте, что серьёзность — не одно число
Исключайте шум С записанной причиной, а не игнорированием
2. Фикс: исправляйте корень, а не прячьте симптом
Первая помощь (сокрытие симптома) и корневое исправление — разные задачи. Сделайте обе, чтобы закончить.
Только сокрытие симптома (сдерживание)
- блокировать только «подозрительно выглядящие» запросы на обратном прокси
- симптом прекратился, значит назвать «разобрались»
- уязвимая зависимость остаётся — RCE живёт дальше
Корневое исправление (правильно)
- обновить уязвимую зависимость до исправленной версии
- после обновления убедиться, что сигнатура исчезла из логов
- считать первую помощь и корневое исправление двумя разными задачами и делать обе
Проверяйте мажорные обновления сборкой ДО продакшена
В отличие от патча, скачок мажорной версии (фреймворк 14→15, UI-кит 4→6 и т. п.) несёт ломающие изменения. Обновить вслепую и сломать прод-сборку — худший случай. Поместите изменённый исходник в идентичную проду среду выполнения (контейнер с той же версией Node/PHP) и добейтесь полностью зелёной сборки/проверки типов до push. Устраняйте ошибки по одной (codemod sync→async, многострочные строки CSS-классов, упразднённое глобальное пространство типов и т. п.). Если старый контейнер продолжает работать, при сбое откат проходит без простоя.
Завершённость включает устойчивость исправления
Идеальное исправление стоит ноль, если следующий деплой его стирает. Это самая частая ловушка.
Не коммитьте в продакшен-дереве
Стандартизируйте одно направление: локально→push→деплой
Не доверяйте HTTP 200 как «здоровью»
curl | grep), нет ли новых ошибок в логах, и пройдите по динамическим маршрутам (на БД, с параметрами), а не только по главной. Кэши могут задержать изменение, так что подождите или сначала очистите.3. Изоляция/передача: сделайте неисправимое явным
Не всегда можно исправить всё сейчас. Хитрость — никогда не оставлять «не могу исправить / не моя зона / не используется» неопределённым.
Осиротевший/EOL код: изолируйте ПЕРЕД удалением
Сначала убедитесь, что на него реально нет ссылок
Явно передавайте то, что не можете исправить
4. Мониторинг: готово только с ежедневным обнаружением изменений
Теперь, наконец, поставьте механизм срабатывания. Без него сделанное исправление не сообщит вам, когда оно повторится.
Оповещайте о НОВОМ, а не обо всём каждый день
Сравнивайте результаты скана с предыдущим прогоном (файлом состояния) и уведомляйте только при появлении нового критического/высокого. Одно и то же содержимое каждый день начинают игнорировать (усталость от оповещений). Сводите в одно письмо (новые / устранённые / текущие) по ежедневному cron. Даже шаред-хостинг нормально тянет один бинарник сканера + cron (нагрузка — миллисекунды; добавьте nice для спокойствия).
«Секреты» и «ключи», которые инвентаризация тоже всплывает
Реальное закрытие CVE склонно всплывать и не-зависимостные дыры. Две классики — файл секрета, оставленный в публичной директории (старый токен в веб-корне; если он из общего шаблона, у каждого сайта та же дыра) и корневой ключ, выданный среде, которую можно скомпрометировать. Оба создают паттерн «одна утечка — всё», так что проверяйте их в том же проходе (каждое заслуживает отдельного разбора). По основам секретов см. безопасное хранение секретов и базовый чек-лист.
Читать дальше
- Мониторинг зависимостей: установка и использование osv-scanner · не отставать от CVE
- Инцидент: заброшенная публичная RCE и мошеннические счета
- Эксплуатация: продакшен только на приём / самостоятельный Git против GitHub
- Основа: базовый чек-лист безопасности
FAQ
QКогда работа с уязвимостями действительно «завершена»?
Только когда сходятся четыре вещи: 1) вы просканировали и перевели состояние в цифры, 2) вы исправили не-dev критические/высокие, 3) вы явно изолировали или передали то, что не можете исправить / осиротевший код, и 4) вы наладили ежедневное обнаружение изменений (мониторинг, ловящий новые и повторяющиеся проблемы). Пока нет п. 4, это не готово — зависимости снова станут уязвимыми завтра.
QНе вызовет ли ежедневное сканирование усталость от оповещений?
Оповещение обо всём ежедневно быстро начинают игнорировать. Сравнивайте с предыдущим результатом (файлом состояния) и уведомляйте только при появлении НОВОГО критического/высокого — обнаружение изменений. Не отправлять одно и то же каждый день — вот что делает мониторинг по-настоящему долговечным.
QПочему исправление иногда откатывается к уязвимому?
Коммит прямо в продакшен-дереве означает, что следующий деплой (pull или checkout -f) перезаписывает и стирает исправление. Стандартизируйте одно направление — локально→push→деплой — и сделайте продакшен «только на приём». Идеальное исправление в процессе, который его стирает, стоит ноль.