ADG Оставить заявку
Блог Информационная безопасность 5 мин чтения

Supply chain атаки на npm и PyPI осенью 2025: наш ответ через SBOM, зеркала и Sigstore

После серии атак на npm и PyPI ужесточили политики в CI/CD: рассказываем про SBOM, зеркалирование зависимостей и проверку артефактов через Sigstore.

Контекст момента

Новые атаки на supply chain через публичные пакетные репозитории - осень 2025

Осенью этого года supply chain снова напомнил о себе. В октябре прилетело несколько волн: вредоносные пакеты в npm с именами, почти неотличимыми от популярных библиотек, и компрометация сопровождающего крупного PyPI-пакета с последующей публикацией заражённой версии. Подробности разбирали в публичных postmortem'ах, нас они задели косвенно - один из клиентов тянул зависимость из PyPI в prod-pipeline без какой-либо верификации. Инцидента не случилось, но разговор о том, как вообще у них устроен supply chain, стал неотложным.

Что увидели при аудите

Картина типичная, без каких-то особенных провалов - просто дефолтные настройки, которые никто не трогал, пока «и так работает».

  • pip install из публичного PyPI прямо в CI без фиксации хешей и без проверки подписей. requirements.txt с незакреплёнными мажорными версиями в нескольких местах.
  • npm ci из публичного реестра, lock-файл есть, но integrity-поля не проверяются дополнительно. Кастомных политик на уровне registry - ноль.
  • Docker-образы тянутся по тегу, не по дайджесту. latest встречается в нескольких Dockerfile'ах.
  • SBOM отсутствует как класс. Никто не знает, что именно вошло в последний prod-деплой, не считая lock-файлов.

Всё это - не халтура команды. Это просто стандартный 2022 год, оставшийся нетронутым в 2025-м.

Что и зачем меняли

Работали поэтапно, не пытаясь переписать всё разом.

SBOM как базовый артефакт сборки

Первый шаг - начать хотя бы знать, что внутри. Внедрили генерацию SBOM на каждом CI-прогоне: для Python используем syft, для Node - тоже syft с поддержкой package-lock.json, для контейнерных образов - после сборки по финальному слою. SBOM в формате CycloneDX кладётся в артефакты CI и в отдельное хранилище рядом с образом.

Немедленной пользы от этого немного - пока SBOM просто лежит. Польза начинается, когда появляется CVE и нужно быстро ответить на вопрос «нас это касается?». Без SBOM это несколько часов ручной работы; с ним - запрос к базе и ответ за минуты. В ноябре уже пригодится - но это отдельная история.

Зеркалирование зависимостей

Прямая зависимость от публичного PyPI или npm в prod-pipeline - это доверие к внешней инфраструктуре без каких-либо гарантий. Подняли внутренний Nexus Repository с прокси-репозиториями для PyPI и npm. CI теперь смотрит только во внутренний реестр, а наружу ходит только Nexus по явному расписанию.

Это решает несколько проблем сразу: пакеты кешируются и доступны при падении внешнего реестра, есть точка контроля для политик (какие пакеты разрешены, какие заблокированы), и сборка становится воспроизводимой - через месяц тот же pip install даст те же байты.

Настройка заняла день. Неожиданная боль: несколько пакетов тянули зависимости напрямую из GitHub через git+https:// в requirements.txt. Это пришлось разобрать отдельно - либо заменить на версионированный PyPI-пакет, либо перенести в отдельный внутренний репозиторий.

Проверка артефактов через Sigstore

Sigstore - это инфраструктура для подписи и верификации артефактов без управления собственными ключами. Cosign (часть Sigstore) позволяет подписать контейнерный образ после сборки и верифицировать подпись перед деплоем. Подпись привязывается к identity CI-пайплайна через OIDC - никаких долгоживущих ключей, которые можно утащить.

Для образов клиента внедрили такую схему: сборка в CI -> подпись через cosign с OIDC-токеном GitLab -> push образа и подписи в Harbor -> при деплое в Kubernetes - верификация подписи через admission controller (Kyverno политика). Образ без валидной подписи не запускается.

Это не защищает от компрометации самого CI, но существенно поднимает планку: случайный или намеренно подменённый образ из внешнего источника просто не пройдёт.

Для npm и PyPI пакетов Sigstore тоже движется в эту сторону - npm уже публикует provenance для части пакетов, PyPI запустил поддержку Trusted Publishers. Пока это не повсеместно, поэтому для зависимостей основная защита - хеши в lock-файлах и проверка через pip install --require-hashes.

Что осталось

Честно - сделано примерно половина из того, что хочется.

Политики разрешённых пакетов в Nexus пока не настроены: зеркало работает, но не фильтрует. Это следующий шаг - поднять список явно запрещённых пакетов и добавить проверку новых зависимостей через review-процесс.

SBOM-сканирование на CVE автоматически в CI не запущено. Есть ручной процесс, нет автоматического gate'а. Grype подключить технически несложно - вопрос в том, как обрабатывать false positives, чтобы не заблокировать pipeline мусором.

Cargo (Rust) в заголовке поста не случайно - у того же клиента есть небольшой Rust-компонент, и supply chain там отдельная тема. cargo-audit запущен, crates.io не зеркалируется, а Sigstore для Rust-артефактов только начинает появляться. Пока это технический долг, обозначенный в отчёте.

Для аудита безопасности такой поэтапный подход - норма: сначала видимость (SBOM), потом контроль точек входа (зеркала), потом верификация артефактов. Не всё за один спринт, но каждый шаг снижает реальную поверхность атаки, а не только закрывает пункты в чеклисте.

Контакт

Нужна такая же инженерная работа?

Опишите задачу и контекст. Ответим в течение рабочего дня, при необходимости подпишем NDA.