Атаки на цепочку поставок ПО и как от них защититься
Продвинутое4 минРедакция Innovialabs
Коротко
Атака на цепочку поставок бьёт не по вашему коду, а по тому, из чего он собирается, — пакетам, инструментам и CI; защита строится на фиксации версий, запрете install-скриптов, SBOM, проверке подписей и минимальных правах в CI.
Коротко
Атака на цепочку поставок (supply chain attack) — это когда злоумышленник не взламывает ваше приложение напрямую, а подменяет то, из чего оно собирается: npm- или PyPI-пакет, GitHub Action, Docker-образ, инструмент сборки. Вредоносный код попадает в проект через обычный npm install и выполняется с правами разработчика или CI.
Основа защиты:
- Фиксировать точные версии и источники зависимостей.
- Не запускать чужие скрипты установки без необходимости.
- Знать состав сборки — SBOM.
- Проверять подписи и происхождение артефактов.
- Ограничить права CI, чтобы взлом одного шага не давал доступа ко всему.
Как атакуют
Тайпсквоттинг. Пакет с именем, похожим на популярный: опечатка, лишний дефис, другой порядок слов. Достаточно ошибиться одной буквой в npm install.
Dependency confusion. Если у компании есть внутренний пакет, например billing-utils, атакующий публикует пакет с тем же именем в публичный реестр с более высокой версией. Неправильно настроенный менеджер пакетов выберет публичный.
Взломанный мейнтейнер. Злоумышленник получает доступ к аккаунту автора популярной библиотеки или становится со-мейнтейнером, постепенно заслужив доверие. Известные примеры — event-stream в npm, куда новый мейнтейнер добавил зависимость с вредоносным кодом, и бэкдор в xz-utils, обнаруженный в 2024 году.
Вредоносные install-скрипты. Поля preinstall и postinstall в package.json выполняются автоматически при установке. Скрипт может прочитать .env, SSH-ключи, токены облака и отправить их наружу — ещё до того, как вы запустили приложение.
Подмена в CI. Сторонний GitHub Action, подключённый по тегу вроде @v3, можно изменить, перенаправив тег на другой коммит. Все пайплайны, которые используют этот тег, выполнят новый код с доступом к секретам.
Фиксация версий и источников
- Коммитьте lock-файл и устанавливайте через
npm ci— он не меняет версии. - Используйте scoped-пакеты для внутренних библиотек и явно привязывайте scope к своему реестру:
# .npmrc
@company:registry=https://npm.company.internal/
- Не обновляйтесь на версию, вышедшую час назад: вредоносные релизы часто обнаруживают и удаляют в первые дни. В Renovate для этого есть настройка
minimumReleaseAge. - В CI подключайте Actions по полному SHA коммита, а не по тегу, и обновляйте их через Dependabot или Renovate.
Install-скрипты
Большинству пакетов скрипты установки не нужны. Отключите их по умолчанию и разрешайте точечно тем, кому они действительно требуются (например, пакетам с нативной сборкой):
npm ci --ignore-scripts
Некоторые менеджеры пакетов позволяют вести явный список пакетов, которым разрешено выполнять скрипты, — пользуйтесь этим.
SBOM
SBOM (Software Bill of Materials) — полный список компонентов сборки с версиями. Он отвечает на вопрос «затронуты ли мы», когда выходит новость о скомпрометированном пакете: вместо ручного поиска — запрос по SBOM всех сервисов.
Стандартные форматы — CycloneDX и SPDX. Сгенерировать SBOM можно, например, так:
npm sbom --sbom-format cyclonedx > sbom.json # в новых версиях npm
syft dir:. -o cyclonedx-json > sbom.json # для любого проекта или образа
Генерируйте SBOM в CI на каждую сборку и храните рядом с артефактом.
Проверка подписей
npm audit signaturesпроверяет подписи пакетов из реестра и provenance-аттестации — подтверждение того, из какого репозитория и пайплайна собран пакет.- Публикуя свои пакеты, используйте
npm publish --provenanceиз CI. - Для контейнеров используйте Sigstore cosign: подписывайте образы при сборке и проверяйте подпись перед деплоем.
cosign verify \
--certificate-identity-regexp "https://github.com/org/app/" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/org/app:1.4.0
Защита CI
| Мера | Что даёт |
|---|---|
Минимальные permissions для токена CI (contents: read по умолчанию) | Скомпрометированный шаг не сможет писать в репозиторий |
| Секреты только в нужных job’ах и environments | Утечка ограничена одним этапом |
Осторожность с pull_request_target и кодом из форков | Чужой код не получает доступ к секретам |
| Эфемерные раннеры | Вредонос не остаётся между сборками |
| Защищённые ветки и обязательное ревью | Нельзя тихо изменить пайплайн |
| OIDC вместо долгоживущих облачных ключей | Нечего украсть надолго |
Для систематической оценки зрелости полезны фреймворк SLSA и инструмент OpenSSF Scorecard.