Серверы
Уязвимости зависимостей: composer, npm и безопасность цепочки поставок
Даже если ваш код чист, устаревшая библиотека делает его уязвимым. composer audit, npm audit, lock-файлы, Dependabot, заброшенные пакеты и SBOM — практическое руководство.
· 10 мин. чтения

В современном веб-приложении код, написанный вами, — небольшая часть всего кода. Проект на Laravel приносит в папку vendor сотни пакетов, а простой фронтенд на React в node_modules — ещё больше. Каждый из них написан другими людьми, и в каждом может найтись уязвимость.
Поэтому безопасность не заканчивается проверкой собственного кода. В этой статье покажем, как находить уязвимости в зависимостях, как их обновлять и как автоматизировать этот процесс.
В чём проблема
Есть два вида риска:
- Известные уязвимости. В библиотеке находят уязвимость, ей присваивают CVE и выпускают исправленную версию. Если вы остались на старой версии, уязвимость открыта, а её детали известны всем. В OWASP Top 10 (2021) это отдельная категория: A06 — Vulnerable and Outdated Components.
- Атаки на цепочку поставок (supply chain). Вредоносным оказывается сам пакет: его имя сделано похожим на популярный пакет (typosquatting), аккаунт автора захвачен и в новую версию добавлен вредоносный код, или скрипт установки выполняет код на сервере либо на компьютере разработчика.
Первый риск привычен, и управлять им просто. Второй встречается реже, но его сложнее обнаружить.
Код, который вы не писали, — тоже ваш код. Для экспертизы
vendorиnode_modules— часть приложения.
Почему это важно для экспертизы
Сканеры экспертизы определяют версии библиотек, используемых приложением, разными способами:
- JavaScript-библиотеки во фронтенде (например, старые версии jQuery или Bootstrap) определяются по имени файла и комментариям внутри.
- Открытые файлы
composer.lock,package.jsonилиpackage-lock.jsonдают все пакеты и их версии одним списком. - HTTP-заголовки и страницы ошибок могут раскрывать версию фреймворка.
Если версия определена, соответствующие известные уязвимости попадают в отчёт как находки. Поэтому перед подачей нужно проверить зависимости и не оставлять lock-файлы открытыми.
Проверьте:
for f in composer.json composer.lock package.json package-lock.json yarn.lock .env; do
printf '%s ' "$f"; curl -s -o /dev/null -w '%{http_code}\n' "https://site.uz/$f"
done
Все ответы должны быть 404 или 403. Если в Laravel веб-корнем служит папка public/, эти файлы естественным образом остаются снаружи. Если корень настроен неправильно, они будут открыты.
Как проверить самостоятельно
PHP: composer audit
Начиная с Composer 2.4 есть встроенная команда аудита. Она сверяет пакеты из composer.lock с базой известных уязвимостей:
composer audit
composer audit --no-dev # только production-пакеты
composer audit --format=json # для CI
Список устаревших пакетов:
composer outdated --direct
--direct показывает только пакеты, которые вы сами указали в composer.json, — при их обновлении часто обновляются и вложенные зависимости.
JavaScript: npm audit
npm audit
npm audit --omit=dev # только production-зависимости
npm audit --audit-level=high # только high и critical
npm outdated
Чтобы узнать, откуда приходит уязвимый пакет:
npm ls <имя-пакета>
npm audit fix обновляет до совместимых версий. npm audit fix --force может поднять и мажорную версию, что способно сломать приложение, — используйте его только вместе с тестами.
Полезно также проверить подписи пакетов из реестра npm:
npm audit signatures
Универсально: OSV-Scanner
Для проектов, где смешано несколько экосистем, OSV-Scanner проверяет все lock-файлы сразу по открытой базе OSV:
osv-scanner scan source -r .
Flutter и Dart
Для мобильных приложений принцип тот же:
flutter pub outdated
Сторонние SDK в мобильном приложении (аналитика, реклама, платежи) тоже являются зависимостями и могут проверяться на экспертизе.
Lock-файлы: воспроизводимая установка
composer.lock и package-lock.json фиксируют, какие именно версии устанавливаются. Без них при каждой установке могут приехать другие версии — в том числе новые, ещё не проверенные и потенциально вредоносные.
Правила:
- Lock-файлы коммитятся в репозиторий.
- В production и CI устанавливайте из lock-файла, не обновляя его:
composer install --no-dev --prefer-dist --optimize-autoloader
npm ci --omit=dev
npm install может изменить lock-файл, а npm ci строго следует ему и выдаёт ошибку, если он не соответствует package.json.
- Просматривайте изменения lock-файлов на code review. Сотни строк изменений в lock ради маленькой правки — повод задать вопрос.
Автоматизация: Dependabot и Renovate
Ручная проверка полезна один раз, но уязвимости публикуются каждый день. Подключите инструмент, который автоматически предлагает обновления.
Для Dependabot на GitHub — .github/dependabot.yml:
version: 2
updates:
- package-ecosystem: "composer"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 5
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
groups:
minor-and-patch:
update-types: ["minor", "patch"]
Dependabot каждую неделю открывает pull request'ы с обновлениями, а предупреждения безопасности приходят отдельно. Renovate — похожий инструмент, работает и на GitLab, и на других платформах и позволяет тонко настраивать группировку и правила автоматического слияния.
Главное — не копить PR. Заведите привычку раз в неделю просматривать и сливать их; если тесты зелёные, не откладывайте патч-обновления.
Добавьте проверку и в CI, чтобы код с уязвимым пакетом не попал в production:
# Шаги GitHub Actions
- run: composer install --no-interaction --prefer-dist
- run: composer audit --no-dev
- run: npm ci
- run: npm audit --omit=dev --audit-level=high
Заброшенные пакеты
Уязвимость не опубликована, но автор прекратил поддержку пакета — это тоже риск. Когда найдут следующую уязвимость, исправления не будет.
Composer предупреждает о заброшенных (abandoned) пакетах при install и update. В Composer 2.7 и новее аудит проверяет и это, а политику можно задать в composer.json:
{
"config": {
"audit": {
"abandoned": "fail"
}
}
}
В npm устаревшие (deprecated) пакеты при установке выводятся как npm warn deprecated — не игнорируйте эти предупреждения. Для каждого заброшенного пакета найдите замену или удалите его.
Защита цепочки поставок
- Проверяйте имя пакета. Перед добавлением нового пакета посмотрите его официальное имя, число загрузок, репозиторий и последнюю активность. Разница в одну букву — признак typosquatting.
- Ограничьте скрипты установки. Пакеты npm могут выполнять скрипты вроде
postinstall. Если в CI они не нужны, используйтеnpm ci --ignore-scripts(но учтите, что некоторые пакеты без скриптов не работают). - Меньше зависимостей — меньше риска. Не добавляйте пакет ради задачи в одну строку.
- Добавьте SRI к скриптам, загружаемым с CDN. Тогда при подмене файла на CDN браузер его не выполнит:
<script src="https://cdn.example.com/lib.min.js"
integrity="sha384-..."
crossorigin="anonymous"></script>
- Не собирайте проект на production-сервере. Собирайте фронтенд в CI и выкладывайте на сервер готовый артефакт —
node_modulesи инструменты сборки на сервере не нужны.
SBOM: знайте, из чего состоите
SBOM (Software Bill of Materials) — список всех компонентов приложения и их версий в стандартном формате (CycloneDX или SPDX). Когда публикуется новая уязвимость, при наличии SBOM вы за несколько секунд отвечаете на вопрос «есть ли у нас этот пакет и какой версии?».
# npm (npm 10+)
npm sbom --sbom-format cyclonedx > sbom-npm.json
# Composer (через плагин CycloneDX)
composer require --dev cyclonedx/cyclonedx-php-composer
composer CycloneDX:make-sbom --output-file=sbom-php.json
Храните SBOM вместе с каждым релизом. Это полезно и при экспертизе, и в работе с заказчиками: вы можете точно показать состав приложения.
Короткий чек-лист
composer audit --no-devиnpm audit --omit=devбез серьёзных уязвимостей.composer.lockиpackage-lock.jsonв репозитории, в production используютсяcomposer installиnpm ci.- Lock-файлы,
package.json,.envне открываются через браузер. - Во фронтенде нет старых jQuery, Bootstrap и других библиотек.
- Подключён Dependabot или Renovate, PR регулярно просматриваются.
- CI останавливает сборку при уязвимой зависимости.
- Заброшенные и deprecated пакеты заменены.
- У скриптов с CDN есть SRI, на production-сервере ничего не собирается.
- Для каждого релиза создаётся SBOM.
Итог
Управление зависимостями — не разовая работа, а процесс. Один раз запустить composer audit и npm audit и закрыть найденное — хорошее начало для экспертизы. Но в долгосрочной перспективе lock-файлы, автоматические обновления и проверки в CI держат приложение в безопасном состоянии каждый день. О ключах и паролях, которые хранятся в коде, мы писали в статье управление секретами и ключами.
Хотите узнать, в каком состоянии зависимости и настройки сервера вашего проекта? Оставьте заявку на сканирование — агент Claude AI и наш специалист изучат кодовую базу и конфигурацию, а мы поможем закрыть найденные дыры и подготовиться к экспертизе.


