Перейти к содержимому

Атаки на цепочку поставок: как защитить проект и production от чужого кода

Чужой код в Production: как работают атаки на цепочку поставок и как защитить проект

Что может быть привычнее для разработчика, чем установить новую библиотеку одной командой: `npm install`, `pip install`, `uv add`, `bundle add` или `cargo add`? Пакетный менеджер быстро находит нужный модуль, разрешает зависимости и загружает вместе с ним десятки, сотни, а иногда и тысячи других компонентов.

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

Пока пакет ведёт себя ожидаемым образом, риск остаётся незаметным. Однако компрометация одного элемента цепочки может превратить стандартное обновление зависимости в доставку вредоносной программы сразу в сотни и тысячи проектов. Такой сценарий называют атакой на цепочку поставок - Supply Chain Attack.

Почему атакующему не обязательно взламывать ваше приложение

Злоумышленнику необязательно искать уязвимость в бизнес-логике или атаковать сервер напрямую. Иногда достаточно получить доступ к аккаунту владельца небольшой, но популярной библиотеки, изменить исходный код и выпустить новую версию в официальном реестре.

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

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

Известные сценарии атак

Один из наиболее показательных случаев - Event Stream. В его зависимость добавили обфусцированный вредоносный код. Пакет скачивали более восьми миллионов раз, а вредоносная часть была рассчитана на кражу криптовалюты у ограниченной группы пользователей.

Другой пример - Shai-Hulud, червь, ориентированный на токены GitHub и npm. Он заражал окружение разработчика, похищал секреты и пытался самостоятельно публиковать заражённые пакеты. По различным оценкам, осенью 2025 года пострадало от 150 до 500 библиотек.

В Ruby-экосистеме встречался другой подход: вредоносный код загружался с Pastebin и выполнялся через `eval` в устаревшей минорной версии Rest Client. Особенно опасно то, что в среде разработки зловред мог бездействовать, чтобы обнаружить себя уже после попадания приложения в эксплуатацию.

В Python известен случай с Torchtriton. Злоумышленник опубликовал в публичном PyPI пакет с тем же именем, что и зависимость, использовавшаяся в отдельных сборках PyTorch. Сам PyTorch не был взломан: проблема возникла из-за того, что `pip` выбрал пакет из центрального реестра.

В Rust злоумышленники применяли тайпсквоттинг - регистрировали библиотеки с названиями, похожими на настоящие: `faster_log` вместо `fast_log`, `async_println` вместо `async-println`. Они копировали документацию и API оригиналов, поэтому подмену было трудно заметить при беглом просмотре `Cargo.toml`. Вредоносный код запускался только во время работы программы и мог похищать приватные ключи Solana и Ethereum.

Где начинается и заканчивается цепочка доверия

Разработчик обычно выбирает одну библиотеку, но фактически получает целое дерево компонентов. Прямая зависимость может подтянуть десять транзитивных пакетов, а те - ещё десятки.

Именно транзитивные зависимости особенно опасны: их часто никто не выбирал осознанно, не изучал и не отслеживал отдельно. При этом они получают доступ к окружению приложения, процессу сборки или данным, которые доступны основному проекту.

Цепочка поставок включает:

- исходный код;
- аккаунт сопровождающего;
- систему контроля версий;
- CI/CD-конвейер;
- процедуру сборки;
- пакетный реестр;
- зеркало или прокси;
- локальный кэш;
- сервер сборки;
- конечное приложение.

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

Достаточно ли сверять хеши

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

Надёжнее использовать lock-файлы, фиксировать точные версии, проверять подписи артефактов и сохранять проверенные пакеты во внутреннем репозитории. Важно контролировать не только сам архив, но и происхождение сборки: кто её создал, из какого коммита и каким инструментом.

Поможет ли Nexus или другой внутренний реестр

Прокси-репозиторий вроде Nexus, Artifactory или аналогичного решения снижает риски, но не делает инфраструктуру безопасной автоматически. Он позволяет:

- ограничить прямой доступ сборок к публичным реестрам;
- кэшировать проверенные версии;
- блокировать подозрительные пакеты;
- проводить внутреннее сканирование;
- контролировать разрешённые источники;
- сохранять артефакты после проверки.

Но если заражённая версия впервые попала во внутренний репозиторий без анализа, она будет считаться доверенной. Поэтому прокси должен дополняться политиками допуска, журналированием, антивирусной и поведенческой проверкой, а также ручным расследованием критичных обновлений.

Обновляться сразу или подождать

Автоматическое обновление всех зависимостей в день выхода новой версии удобно, но рискованно. Чем выше критичность проекта, тем важнее вводить задержку и поэтапную проверку.

Практичный подход:

1. новые версии сначала попадают в отдельную ветку;
2. CI запускает тесты и сканеры;
3. изменения в манифесте анализируются отдельно;
4. обновление проходит тестовую среду;
5. затем внедряется в ограниченную группу сервисов;
6. после наблюдения распространяется на весь Production.

Для библиотек, которые обрабатывают платежи, токены, криптографию или персональные данные, полезно требовать дополнительное ревью даже при небольшом обновлении.

На что обращать внимание при проверке пакета

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

Также стоит анализировать:

- скрипты `preinstall`, `postinstall` и аналогичные хуки;
- вызовы `eval`, `exec` и запуск shell-команд;
- загрузку кода с внешних адресов;
- обфускацию;
- изменения в сборочном процессе;
- попытки обращения к домашнему каталогу;
- работу с SSH-ключами и токенами;
- несоответствие между репозиторием и опубликованным архивом.

Одного признака недостаточно, но совокупность таких сигналов должна запускать дополнительную проверку.

Что делать, если зависимость уже оказалась заражённой

Сначала нужно остановить распространение: заморозить сборки, отключить автоматические публикации и исключить подозрительную версию из всех каналов доставки.

Затем следует:

1. определить период, в котором использовался заражённый пакет;
2. найти все сборки и окружения, где он присутствовал;
3. проверить логи установки, сборки и запуска;
4. отозвать токены, пароли, SSH-ключи и сертификаты;
5. заменить потенциально скомпрометированные ключи;
6. собрать приложение из заведомо чистых версий;
7. проверить исходящий трафик и необычные процессы;
8. зафиксировать результаты расследования.

Простого удаления пакета недостаточно. Если зловред успел прочитать секрет из переменной окружения, он может оставаться у атакующего даже после удаления зависимости.

Как выстроить защиту системно

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

Минимальный набор мер включает:

- lock-файлы и воспроизводимые сборки;
- запрет установки произвольных пакетов напрямую из интернета;
- внутренний реестр артефактов;
- сканирование зависимостей и лицензий;
- автоматическое обнаружение секретов;
- минимальные права для CI-токенов;
- короткий срок жизни ключей;
- обязательное ревью изменений зависимостей;
- изоляцию сборочных агентов;
- мониторинг исходящих соединений;
- регулярную ротацию секретов;
- аварийный план реагирования.

Особое внимание следует уделять CI/CD. Сборочный агент часто имеет доступ одновременно к исходному коду, секретам, реестру контейнеров и Production. Если вредоносный пакет выполняется в процессе сборки, он может использовать все эти полномочия.

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

Главный принцип

Supply Chain Attack - это не проблема только безопасности или только разработки. Она возникает на пересечении кода, инфраструктуры, процессов публикации и управления доступом.

Нельзя полностью отказаться от сторонних библиотек, но можно сделать доверие проверяемым: фиксировать версии, контролировать происхождение артефактов, ограничивать права, наблюдать за поведением программ и иметь готовый сценарий отзыва секретов.

Чужой код неизбежно присутствует почти в любом современном Production. Вопрос не в том, можно ли доверять каждой строке, а в том, насколько быстро команда заметит нарушение доверия и сможет ограничить последствия.

Прокрутить вверх