Dependency Confusion: как коллизия имён в реестрах превращается в RCE и что с этим делать
Механика private/public name collision, реальные взломы и практический список защит — от naming-конвенций до review-политики для каждого PR
Почему это важно прямо сейчас
Dependency confusion — не экзотика и не академический эксперимент. OWASP включил Software Supply Chain Failures в проект своего Top 10 2025 сразу на третью строку [4]. Verizon DBIR 2025 зафиксировал удвоение доли взломов через третью сторону: с 15% до 30% за год [5].
При этом сама механика атаки принципиально не изменилась с 2021 года — изменился контекст: команды стали быстрее, CI/CD плотнее, а AI-ассистенты добавили новый источник фантомных зависимостей прямо в pull request. Если ваш разработчик принял PR с кодом от Copilot, не проверив имена пакетов, атака может пройти до merge — тихо, без алертов.
Что такое dependency confusion: механика name collision
Каждый package manager — npm, pip, RubyGems, NuGet, Maven — умеет работать с несколькими источниками одновременно: приватным корпоративным реестром и публичным (npmjs.com, pypi.org и т.д.). Именно в этом «одновременно» и зарыта проблема.
Когда build system запрашивает пакет и находит его и в приватном реестре, и в публичном, она руководствуется простым правилом: берётся версия с наибольшим номером. Это поведение по умолчанию у npm, pip при использовании --extra-index-url и у ряда прокси-решений вроде JFrog Artifactory и Sonatype Nexus при конфигурации виртуального репозитория [6].
Схема атаки пошагово:
- Атакующий находит имя приватного пакета. Источников много:
package.jsonв публичном GitHub-репозитории, сообщения об ошибках npm, вакансии, внутренняя документация, случайно опубликованные конфиги [1][7]. - Публичный реестр не содержит такого пакета — имя свободно.
- Атакующий регистрирует пакет с тем же именем и устанавливает версию 9.9.9 или выше — заведомо выше реальной.
- В пакет добавляется
postinstallскрипт, который выполняется сразу после установки: эксфильтрация данных, reverse shell или маячок через DNS [1]. - Следующий
npm install/pip installв CI/CD или на машине разработчика тянет публичный пакет — build system «выбирает лучшее».
Именно так Бирсан попал внутрь Apple, Microsoft, Netflix, Uber и ещё 30 компаний. Из сетей Apple выполнялся код на разработческих машинах darwin x64; из инфраструктуры Netflix запросы приходили с macOS CI-раннеров [1][7].
Откуда берутся имена приватных пакетов
Это ключевой вопрос — без имени атаки нет. Источники обнаружения приватных имён:
package.json,requirements.txt,pom.xmlи другие манифесты в публичных репозиториях на GitHub/GitLab;- сообщения об ошибках npm, которые могут раскрывать имя registry;
- внутренние пакеты, случайно опубликованные на публичных реестрах;
- сообщения на форумах и Stack Overflow с фрагментами кода;
- вакансии, где упоминаются внутренние библиотеки;
- JS source maps и bundle-файлы, которые доступны в продакшне [11].
pip: особый случай с --extra-index-url
В Python ситуация хуже, чем в npm. Флаг --extra-index-url по замыслу добавляет индекс в дополнение к дефолтному pypi.org — и pip выбирает пакет с максимальным номером версии из всех индексов. Это «небезопасно по дизайну», как описал сам Бирсан [10]. Правильная альтернатива — --index-url (единственный источник). Использование --extra-index-url с приватным реестром при наличии одноимённого пакета в PyPI — прямой путь к компрометации.
Реальные кейсы: от исследования до продакшн-взломов
2021: Бирсан и 35 компаний
Алекс Бирсан в феврале 2021 года опубликовал writeup о том, как методично взломал более 35 крупных организаций [1]. Bounty-выплаты: Microsoft — $40 000, Apple, Shopify, PayPal — по $30 000 каждая. Суммарно исследователь заработал более $130 000. Microsoft присвоила проблеме CVE-2021-24105 и опубликовала whitepaper с тремя стратегиями защиты [8].
2024: Netflix — developer laptop как точка входа
В октябре 2024 года через атаку dependency confusion был достигнут RCE в инфраструктуре Netflix. Пакет nf-cl-logger содержал типичный Netflix-префикс nf и не существовал в публичном npm. После публикации в течение 36 часов пришли запросы с IP-адресов Netflix Streaming Services — с машин darwin x64, то есть с ноутбуков разработчиков или ephemeral macOS CI-раннеров [9].
2024: Apache archived project
В марте 2024 года команда Legit Security обнаружила dependency confusion в архивированном проекте Apache. Команда безопасности Apache приняла стандартное решение: зарегистрировать публичную-«пустышку» с тем же именем, чтобы заблокировать вектор захвата имени [2].
2022: PyTorch — атака через частичное имя
В декабре 2022 года пакет torchtriton был подменён через PyPI. Разработчики PyTorch, установившие nightly-сборку, получили малварь, которая собирала SSH-ключи и другие чувствительные данные. PyTorch отреагировал, изменив схему именования пакетов [10].
2025: slopsquatting как новое поколение confusion-атак

Исследование трёх университетов — University of Texas at San Antonio, University of Oklahoma, Virginia Tech — в 2025 году показало: LLM-ассистенты галлюцинируют несуществующие пакеты в ~20% случаев для open-source моделей и ~5% для коммерческих [3][16]. Среди 576 000 сгенерированных кодовых сэмплов было обнаружено более 200 000 уникальных несуществующих имён пакетов, причём 58% повторялись хотя бы дважды в 10 запусках — то есть это не случайный шум, а предсказуемые артефакты, которые можно картировать и занять превентивно [18].
Механика та же, что у классического dependency confusion: атакующий наблюдает, какие имена галлюцинирует конкретная модель для конкретных промптов, регистрирует эти пакеты заранее и ждёт, когда разработчик запустит pip install на код от ИИ-ассистента.
Почему стандартные конфигурации не защищают
Типичная ошибка — считать, что приватный реестр автоматически означает безопасность. Это не так. Проблемы возникают из-за:
Hybrid feed configuration. JFrog Artifactory, Sonatype Nexus, Azure Artifacts — все эти инструменты поддерживают виртуальные репозитории, которые агрегируют локальные и удалённые (публичные) источники под одним URL. Если не настроены exclude patterns или priority rules, пакет с высокой версией из публичного реестра победит приватный [20].
Параллельное разрешение. Часть package managers при использовании нескольких индексов опрашивает их параллельно и берёт максимальную версию — независимо от источника [6].
Отсутствие lock-файла. Версии без pin — ^1.0.0 или >=2.0 — позволяют установить любую более высокую версию. Если атакующий публикует 99.0.0, build system выберет именно её [13].
Кэш CI/CD. Build агенты, которые не сбрасываются до начального состояния после каждого билда, могут кэшировать скомпрометированные пакеты и распространять их дальше [11].
Практический список защит
Ни одна мера не является серебряной пулей. Безопасная конфигурация — это несколько слоёв, каждый из которых перекрывает слепые пятна предыдущего.
1. Naming: scoped packages и внутренние конвенции
Первый и самый важный слой. Организационный скоуп в npm (@company/package-name) означает, что namespace принадлежит вашей организации — никто другой не может опубликовать пакет в этом скоупе [4]. Аналоги есть в других экосистемах: NuGet поддерживает ID Prefix Reservation, Maven — Group ID.
Ключевые правила:
- Зарегистрируйте скоуп на публичном реестре, даже если не планируете публиковать туда пакеты.
- Применяйте скоуп ко всем внутренним пакетам без исключений — включая legacy и deprecated.
- В
.npmrcявно привяжите скоуп к приватному реестру:@company:registry=https://your.registry.example.com. - Если legacy-код использует неscoped имена — зарезервируйте эти имена на публичном реестре, опубликовав пустой placeholder-пакет с описанием и ссылкой на внутреннюю документацию [14].
Atlassian стала жертвой Бирсана именно потому, что использовала корпоративный скоуп внутри, но не зарегистрировала его на npm — исследователь занял его первым [10].
2. Registry pinning: один источник истины
Microsoft в своём whitepaper называет это первым приоритетом: использовать единственный приватный feed, который сам проксирует нужные публичные пакеты [8]. Это означает:
- Все зависимости — и публичные, и приватные — резолвятся через ваш registry (Artifactory, Nexus, AWS CodeArtifact, Azure Artifacts, GitHub Packages).
- Package manager настроен на единственный источник, публичный интернет недоступен напрямую из CI/CD.
- Если registry временно недоступен — build падает, а не фолбэчится на публичный реестр.
Для pip: используйте только --index-url, никогда --extra-index-url для приватных пакетов. Для npm: registry= в .npmrc без дублирования источников.
3. Allowlist и exclude patterns в registry
Если вы используете hybrid feed (Artifactory virtual repo, Nexus group), настройте exclude patterns: явно укажите, какие имена пакетов никогда не должны резолвиться из публичного upstream [20].
- В Artifactory: exclude patterns на remote repository блокируют запросы к публичному реестру для перечисленных имён.
- В Nexus: аналогичная конфигурация через Content Selectors.
- Принцип allowlist: публичный upstream разрешён только для явно одобренных пакетов и версий. Всё остальное — только из локального хранилища.
Google Cloud Artifact Registry в своей документации прямо указывает: инструменты, которые не позволяют контролировать порядок поиска при смешанной конфигурации, уязвимы к dependency confusion, и рекомендует использовать remote + virtual repository именно для минимизации этого риска [19].
4. Version pinning и integrity verification
Фиксация конкретной версии в lock-файле — не панацея, но существенно сужает окно для атаки:
- Используйте
package-lock.json(npm) илиyarn.lock,poetry.lock(Python),Gemfile.lock(Ruby) — и коммитьте их в репозиторий. - В CI/CD используйте
npm ciвместоnpm install: команда устанавливает строго то, что указано в lock-файле, без обновления версий. - Lock-файл содержит поле
integrity— хэш (SRI) tarball-файла. Если пакет подменён, хэш не совпадёт и установка упадёт [16]. - Pinning защищает от автоматической установки более высокой версии, но не защищает первую установку на пустом окружении без lock-файла.
5. Install policy: postinstall scripts и egress
Postinstall скрипты — любимый инструмент атакующих. Именно они позволили Бирсану мгновенно получить DNS-эксфильтрацию после установки [1][12]:
- Запретите выполнение lifecycle scripts при установке:
npm config set ignore-scripts true. Это глобальная настройка для CI; на machine разработчика — отдельное решение с компромиссами [17]. - Для pip: используйте
--no-build-isolationи--require-hashesдля строгой верификации. - На уровне сети: ограничьте исходящие соединения из CI/CD. Build environment не должен иметь произвольного доступа в интернет. Соединения с публичными реестрами — только через ваш proxy/registry.
- Мониторьте DNS-запросы из build-окружения: внезапные внешние DNS-запросы при установке пакетов — индикатор компрометации.
6. Code review зависимостей: gate до merge
Технические меры не закрывают человеческий фактор: разработчик добавил новый пакет, PR прошёл ревью кода логики, но никто не посмотрел на изменения в package.json внимательно. Особенно это актуально при использовании AI-ассистентов, которые могут предложить несуществующий пакет [18][15]:
- Каждое изменение в
package.json,requirements.txt,go.mod,pom.xmlи lock-файлах должно проходить явный security review, отдельный от логики кода. - Проверяйте: существует ли пакет в публичном реестре? Когда он был создан? Кто maintainer? Совпадает ли имя с внутренними библиотеками?
- Для пакетов, добавленных AI-ассистентами: верифицируйте каждое имя вручную — не доверяйте тому, что пакет «звучит правильно».
- Инструменты SCA (Snyk, Socket, OWASP Dependency-Check, Dependabot) умеют детектировать потенциальные dependency confusion: незарегистрированные scoped packages, пакеты с незаявленными высокими версиями, новые пакеты с именами, схожими с внутренними [12].
Именно здесь PR-review превращается в security gate: новая или обновлённая зависимость в диффе — сигнал, требующий отдельного внимания, независимо от того, кто написал код — человек или ИИ.
Как это работает в разных экосистемах
npm / Node.js
Наиболее задокументированная экосистема. Уязвимость возникает при конфигурации нескольких source registry без явного scope pinning [4]:
# .npmrc — безопасная конфигурация
@company:registry=https://your.private.registry.com
registry=https://registry.npmjs.orgПри такой конфигурации @company/* пакеты разрешаются только из приватного реестра. Пакеты без скоупа — из npm. Риск: пакеты без скоупа по-прежнему могут стать жертвой confusion, если их имена совпадают с внутренними.
Python / pip
Самая опасная конфигурация — --extra-index-url. Безопасная альтернатива:
pip install --index-url https://your.private.registry.com/simple/ package-nameЕщё надёжнее — --require-hashes в сочетании с полным lock-файлом (pip-tools, Poetry).
Maven / Java
Group ID работает как namespace. Зарегистрируйте свой Group ID на Maven Central. Используйте <repositories> с явным указанием <releases> и <snapshots> и не добавляйте публичные репозитории в settings.xml без необходимости.
NuGet / .NET
NuGet поддерживает ID Prefix Reservation на nuget.org. Зарезервированный префикс визуально отмечается на странице пакета. Microsoft рекомендует это как один из трёх основных методов защиты [8][11].
Чек-лист: что проверить прямо сейчас
Naming:
- [ ] Все внутренние пакеты используют скоуп/namespace.
- [ ] Скоуп зарегистрирован на публичном реестре.
- [ ] Unscoped внутренние пакеты зарезервированы как placeholders.
Registry:
- [ ] CI/CD использует единственный registry-источник.
- [ ] Приватный registry не фолбэчится на публичный при недоступности.
- [ ] Virtual/group репозиторий настроен с exclude patterns для внутренних имён.
- [ ]
--extra-index-urlв pip не используется для внутренних пакетов.
Versioning:
- [ ] Lock-файлы коммитятся в репозиторий.
- [ ] CI использует
npm ci/pip install --require-hashes. - [ ] Integrity-хэши верифицируются при установке.
Install policy:
- [ ]
ignore-scripts=trueв CI/CD конфигурации npm. - [ ] Egress из build environment ограничен.
- [ ] DNS-аномалии в build окружении мониторятся.
Review:
- [ ] Изменения в dependency-файлах проходят явный security review.
- [ ] SCA инструмент интегрирован в pipeline и проверяет новые зависимости.
- [ ] Пакеты из AI-сгенерированного кода верифицируются вручную.
- [ ] Внутренние пакетные имена не присутствуют в публичных репозиториях и конфигах.
Ошибки, которые совершают даже опытные команды
«Приватный registry — значит защищены». Нет. Hybrid feed без правильных exclude patterns остаётся уязвимым. Именно так были скомпрометированы многие CI/CD среды.
«У нас lock-файл — мы в безопасности». Lock-файл защищает повторные установки, но не первую. Если разработчик добавил пакет без проверки, lock зафиксирует уже скомпрометированную версию.
«Мы используем скоуп». Если скоуп не зарегистрирован в публичном реестре — его можно занять. Atlassian использовал внутренний скоуп, но не зарегистрировал его на npm. Бирсан занял его и опубликовал туда пакеты [10].
«AI написал — значит пакет существует». LLM hallucinations создают plausible-sounding имена, которых нет в реестрах. Для коммерческих моделей ~5% предложений — несуществующие пакеты [16]. При масштабе команды 50 разработчиков, использующих AI-ассистенты, это десятки потенциальных slopsquatting-мишеней в неделю.
«Мы проверяем PR логики кода». Dependency changes в диффе часто теряются среди сотен строк бизнес-логики. Изменение в package.json — это изменение supply chain, требующее отдельного внимания.
Trend: от разовых атак к автоматизированной охоте
По данным GitGuardian, в течение 48 часов после публикации исследования Бирсана в 2021 году в npm появилось более 275 copycat-пакетов от разных атакующих [14]. Атака оказалась настолько тиражируемой, что её немедленно взяли на вооружение.
В 2025 году GhostAction — атака, зафиксированная GitGuardian 5 сентября — затронула 327 GitHub-пользователей и 817 репозиториев. Через вредоносные workflow злоумышленники эксфильтрировали 3325 секретов, включая токены PyPI, npm и DockerHub [6].
OWASP включил Software Supply Chain Failures в свой Top 10 2025 на третье место, отметив dependency confusion как один из ключевых векторов [14]. Это сигнал: тема перешла из категории «advanced threat» в категорию «baseline hygiene».
Слой контроля перед merge
Описанные выше меры хорошо работают как плановая гигиена, но у них есть общая слепая зона: они не отвечают на вопрос «что именно в этом конкретном PR несёт риск прямо сейчас». Lock-файл может быть корректным вчера и скомпрометированным сегодня, если новый пакет добавлен без проверки.
Именно для этого разрыва существуют инструменты review уровня PR — те, кто анализирует конкретное изменение в контексте: появилась новая зависимость, изменился lock-файл, добавлен postinstall скрипт. Такой подход позволяет не генерировать шум по всему репозиторию, а давать короткий ответ: «вот это изменение требует внимания, вот почему, вот следующий шаг».
HighVibe работает именно на этом уровне — анализирует конкретный PR, смотрит на новые и изменённые зависимости в диффе и помогает команде понять, можно ли безопасно принять изменение до merge, не перекладывая это на длинный SCA-отчёт, который дочитывают не всегда.
Что делать дальше
Dependency confusion — атака, которая не требует от злоумышленника доступа к вашей инфраструктуре. Только правильное имя и публичный реестр. При этом защита достаточно технически несложная — она требует дисциплины, а не экзотических инструментов.
Приоритет действий:
- Проведите инвентаризацию внутренних пакетных имён — особенно тех, что фигурируют в публичных репозиториях, конфигах и сообщениях об ошибках.
- Зарегистрируйте организационный namespace на публичных реестрах. Это бесплатно и занимает минуты.
- Переведите CI/CD на single-source registry с корректными exclude patterns.
- Добавьте проверку dependency-изменений в процесс code review как отдельный явный шаг.
- Верифицируйте пакеты из AI-генерируемого кода — slopsquatting растёт пропорционально росту AI-adoption.
Dependency confusion — не проблема пакетного менеджера. Это проблема доверия к автоматизации без контроля. И чем быстрее pipeline, тем важнее иметь gate, который смотрит на изменение прежде, чем оно уйдёт в продакшн.
Источники
[1] Medium / Alex Birsan — "Dependency Confusion: How I Hacked Into Apple, Microsoft and Dozens of Other Companies" — https://medium.com/@alex.birsan/dependency-confusion-how-i-hacked-into-apple-microsoft-and-dozens-of-other-companies-4a5d60fec610
[2] Legit Security — "Dependency Confusion Vulnerability Found in an Archived Apache Project" — https://www.legitsecurity.com/blog/dependency-confusion-vulnerability-found-in-an-archived-apache-project
[3] BleepingComputer — "AI-hallucinated code dependencies become new supply chain risk" — https://www.bleepingcomputer.com/news/security/ai-hallucinated-code-dependencies-become-new-supply-chain-risk/
[4] npm Docs — "Threats and Mitigations" — https://docs.npmjs.com/threats-and-mitigations/
[5] Cycode — "The 2025 OWASP Top 10: Addressing Software Supply Chain and LLM Risks" — https://cycode.com/blog/the-2025-owasp-top-10-addressing-software-supply-chain-and-llm-risks-with-cycode/
[6] GitGuardian — "Dependency Confusion Attacks and Prevention: Register Your Private Package Names" — https://blog.gitguardian.com/dependency-confusion-attacks/
[7] Arcjet — "Security Concepts for Developers: Dependency Confusion Attacks" — https://blog.arcjet.com/security-concepts-for-developers-dependency-confusion-attacks/
[8] Microsoft Azure — "3 Ways to Mitigate Risk When Using Private Package Feeds" — https://azure.microsoft.com/en-us/resources/3-ways-to-mitigate-risk-using-private-package-feeds/
[9] LandH.tech — "Netflix Vulnerability: Dependency Confusion in Action" — https://www.landh.tech/blog/20250610-netflix-vulnerability-dependency-confusion/
[10] Medium / Alex Birsan — "Dependency Confusion" (оригинальный writeup, pip / --extra-index-url, Atlassian) — https://medium.com/@alex.birsan/dependency-confusion-4a5d60fec610
[11] Security Scientist — "12 Questions and Answers About Dependency Confusion (Supply Chain)" — https://www.securityscientist.net/blog/12-questions-and-answers-about-dependency-confusion-supply-chain/
[12] Gridinsoft — "Researcher compromised 35 companies through new dependency confusion attack" — https://gridinsoft.com/blogs/researcher-compromised-35-companies-through-new-dependency-confusion-attack/
[13] Web Security Lens — "How Dependency Confusion attack works and How to prevent it" — https://www.websecuritylens.org/how-dependency-confusion-attack-works-and-how-to-prevent-it/
[14] GitGuardian — "Dependency Confusion Attacks and Prevention" (GhostAction, copycat packages, OWASP Top 10 2025) — https://blog.gitguardian.com/dependency-confusion-attacks/
[15] Snyk — "Detect and prevent dependency confusion attacks on npm to maintain supply chain security" — https://snyk.io/blog/detect-prevent-dependency-confusion-attacks-npm-supply-chain-security/
[16] Medium / InstaTunnel — "Dependency Confusion: The Supply Chain Attack in your package.json" — https://medium.com/@instatunnel/dependency-confusion-the-supply-chain-attack-in-your-package-json-9517dd17dbe8
[17] GitHub / lirantal — "npm Security Best Practices" — https://github.com/lirantal/npm-security-best-practices
[18] BleepingComputer — "AI-hallucinated code dependencies become new supply chain risk" (повторяемость 58%, данные arXiv) — https://www.bleepingcomputer.com/news/security/ai-hallucinated-code-dependencies-become-new-supply-chain-risk/
[19] Google Cloud Docs — "Remote repositories overview | Artifact Registry" — https://docs.cloud.google.com/artifact-registry/docs/repositories/remote-overview
[20] Foojay — "Namespace Shadowing (a.k.a. 'Dependency Confusion') Attack" — https://foojay.io/today/namespace-shadowing-a-k-a-dependency-confusion-attack/ твует в открытых исследованиях.