OWASP Web Top 10 — A08

Dependency Confusion

Dependency Confusion is a sophisticated supply chain attack that exploits how package managers resolve dependencies. Attackers trick build systems into downloading malicious packages from public repositories instead of legitimate private ones, potentially compromising entire organizations.

What Is Dependency Confusion?

Dependency Confusion, also known as Substitution Attack or Namespace Confusion, was first demonstrated at scale by security researcher Alex Birsan in 2021. The attack exploits a fundamental flaw in how package managers like npm, pip, Maven, and NuGet resolve dependencies when both public and private package repositories are configured. By uploading a malicious package with the same name as a private internal package to a public registry, an attacker can trick the package manager into downloading and executing their code instead of the legitimate internal package.

The vulnerability arises from package managers' default behavior of preferring higher version numbers, regardless of repository source. When a build system encounters a dependency name that exists in both a private internal registry and a public registry like npmjs.com or Maven Central, many package managers will automatically choose the package with the higher version number. An attacker who discovers the names of private packages can publish a malicious package with an artificially inflated version number (like 999.0.0) to the public registry, ensuring their malicious code is installed instead of the legitimate private package.

This attack vector is particularly dangerous because it can bypass traditional security controls. Since the malicious package is installed through the normal dependency resolution process, it may not trigger alerts from security tools. Dependency Confusion has been ranked under A08 (Software and Data Integrity Failures) in the OWASP Top 10 2021, reflecting the critical importance of securing the software supply chain. Alex Birsan's research demonstrated that major companies including Apple, Microsoft, PayPal, Tesla, and Netflix were vulnerable to this attack, earning him over $130,000 in bug bounties.

How It Works

1
Attacker discovers internal package names

The attacker identifies the names of private internal packages used by the target organization. This can be done through reconnaissance techniques such as analyzing public GitHub repositories for dependency declarations, examining JavaScript bundle files for module names, scraping package-lock.json or pom.xml files accidentally committed to public repos, or exploiting misconfigured CI/CD systems that leak build logs.

2
Malicious package is published to public registry

Once internal package names are identified, the attacker creates malicious packages with identical names and publishes them to public registries like npmjs.com, PyPI, or Maven Central. The attacker assigns these packages extremely high version numbers (such as 999.0.0 or 9999.0.0) to ensure they are preferred over legitimate private packages during dependency resolution.

3
Build system resolves dependencies

When the organization's build system attempts to install dependencies, it queries both the private internal registry and the configured public registries. The package manager's default behavior is to compare version numbers across all sources and select the package with the highest version number, regardless of which registry it comes from.

4
Malicious package is installed

Because the attacker's malicious package has a higher version number than the legitimate internal package, the package manager automatically selects and installs the malicious version from the public registry. This happens silently during the normal dependency installation process, without raising any alerts or warnings.

5
Malicious code executes

Once installed, the malicious package's code executes during the build process or at runtime. This code can exfiltrate sensitive environment variables, source code, credentials, or API keys, establish persistent backdoors, modify the build artifacts, or pivot to other internal systems. The attacker now has a foothold in the organization's infrastructure through a seemingly legitimate dependency installation.

Vulnerable Code Example

Vulnerable — Java / Spring Boot (pom.xml)
<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.acmecorp</groupId>
    <artifactId>payment-service</artifactId>
    <version>1.0.0</version>

    <!-- VULNERABLE: No explicit repository priority configuration -->
    <!-- Maven will check Maven Central alongside private registry -->
    <repositories>
        <repository>
            <id>maven-central</id>
            <url>https://repo.maven.apache.org/maven2</url>
        </repository>
        <repository>
            <id>company-nexus</id>
            <url>https://nexus.acmecorp.internal/repository/maven-releases</url>
        </repository>
    </repositories>

    <dependencies>
        <!-- VULNERABLE: Internal package name could be claimed on Maven Central -->
        <!-- No namespace reservation or scoping -->
        <dependency>
            <groupId>com.acmecorp</groupId>
            <artifactId>internal-auth-lib</artifactId>
            <version>2.1.0</version>
        </dependency>
    </dependencies>
</project>

<!-- Attack scenario:
1. Attacker discovers "internal-auth-lib" dependency name
2. Publishes com.acmecorp:internal-auth-lib:999.0.0 to Maven Central
3. Build system downloads version 999.0.0 from Maven Central
   instead of version 2.1.0 from private Nexus
4. Malicious code executes during build or at runtime
-->

Secure Code Example

Secure — Java / Spring Boot (pom.xml with proper scoping)
<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.acmecorp</groupId>
    <artifactId>payment-service</artifactId>
    <version>1.0.0</version>

    <!-- SECURE: Repository mirrors with strict priority -->
    <!-- Force all com.acmecorp.* artifacts through private registry only -->
    <repositories>
        <repository>
            <id>company-nexus</id>
            <url>https://nexus.acmecorp.internal/repository/maven-public</url>
            <!-- This repository proxies Maven Central but blocks external
                 packages with internal namespace -->
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>com.acmecorp</groupId>
            <artifactId>internal-auth-lib</artifactId>
            <version>2.1.0</version>
            <!-- SECURE: Checksum verification enabled by default in Maven -->
        </dependency>
    </dependencies>
</project>

<!-- Additional security measures in settings.xml:

<settings>
    <mirrors>
        <mirror>
            <id>nexus-mirror</id>
            <mirrorOf>*</mirrorOf>
            <url>https://nexus.acmecorp.internal/repository/maven-public</url>
        </mirror>
    </mirrors>

    <!-- Namespace reservation: only allow com.acmecorp.* from private repo -->
    <!-- Configure Nexus to block external packages with internal namespace -->
</settings>

Key protections:
1. Repository mirror forces all downloads through controlled proxy
2. Nexus configured to reject external packages matching internal namespace
3. Checksum verification ensures package integrity
4. Private registry is the only source for internal artifacts
-->

Types of Supply Chain Attacks

Package Manager Confusion

The classic Dependency Confusion attack where package managers like npm, pip, Maven, and NuGet resolve packages from public registries instead of private ones. This occurs when the package manager compares version numbers across multiple sources and chooses the highest version, regardless of source. Attackers exploit this by publishing packages with inflated version numbers (999.0.0) to public registries, ensuring their malicious package is selected over the legitimate internal package. This attack is particularly effective because it leverages the normal dependency resolution mechanism.

Typosquatting

A related attack where adversaries publish malicious packages with names similar to popular legitimate packages, exploiting common typos or naming variations. Examples include crossenv instead of cross-env, or require.js instead of requirejs. Developers who make a typo in their dependency declaration accidentally install the malicious package. Unlike Dependency Confusion which targets private package names, typosquatting targets public packages and relies on human error. The malicious packages often include the functionality of the legitimate package to avoid detection while also executing malicious code.

Namespace Hijacking

An attack where adversaries claim abandoned, expired, or unregistered namespaces on public package registries. This can occur when a company changes its domain name, allowing an attacker to register the old domain and claim the associated namespace on package registries. It also happens with unclaimed organizational namespaces on platforms like npm (before scoped packages were required) or when maintainers abandon packages and let their registry accounts expire. Attackers can then publish malicious updates to previously legitimate packages, affecting all users who depend on that package. This is particularly dangerous because existing projects may automatically receive the malicious updates.

Impact

A successful Dependency Confusion attack can have catastrophic consequences for organizations. Because the malicious code executes in trusted build environments with elevated privileges, attackers gain access to extremely sensitive resources.

Source code and intellectual property theft

Malicious packages executing in build environments have full access to the organization's source code repositories. Attackers can exfiltrate proprietary algorithms, trade secrets, and entire codebases. Because build systems often have read access to multiple repositories, a single compromised dependency can expose years of development work and competitive advantages.

Credential and secrets exposure

Build and deployment pipelines require access to sensitive credentials including cloud provider API keys, database passwords, SSH keys, code signing certificates, and deployment tokens. Malicious packages can scrape environment variables, configuration files, and secret management systems, potentially exposing credentials that grant access to production infrastructure, customer data, and financial systems.

Supply chain compromise and backdoors

Attackers can inject backdoors into build artifacts that are then distributed to customers or deployed to production. This allows persistent access to not only the compromised organization but also all downstream consumers of the software. A single compromised build can affect thousands or millions of end users, as demonstrated in attacks like SolarWinds.

Infrastructure takeover and lateral movement

Build systems often run with elevated privileges in cloud environments and have network access to internal systems. Malicious code can exploit these privileges to provision resources, modify infrastructure, deploy cryptominers, or pivot to other internal systems. Attackers can establish persistent access by creating backdoor accounts, modifying CI/CD configurations, or compromising artifact repositories.

Prevention Checklist

Configure repository mirrors with strict scoping

Set up your package manager to use a private registry mirror (like Nexus, Artifactory, or Azure Artifacts) as the single source for all dependencies. Configure this mirror to proxy public registries while enforcing namespace rules that prevent external packages from claiming internal namespaces. For example, configure your mirror to only allow packages with your organization's namespace (com.yourcompany.*) from your private registry, blocking any external packages that attempt to use that namespace.

Use scoped packages and reserved namespaces

For npm packages, use scoped packages (@yourcompany/package-name) and register your organization's scope on npmjs.com to prevent others from publishing under your namespace. For Maven, claim your organization's groupId namespace on Maven Central. For Python, reserve your organization's namespace on PyPI. For NuGet, reserve package ID prefixes. This prevents attackers from publishing packages that match your internal naming conventions on public registries.

Implement dependency lock files and checksum verification

Always use lock files (package-lock.json, yarn.lock, Pipfile.lock, go.sum) and commit them to version control. These files pin exact versions and checksums of dependencies, preventing unexpected package substitutions. Enable checksum verification in your package manager configuration to ensure downloaded packages match expected hashes. Regularly audit and update these lock files through controlled processes rather than allowing automatic updates.

Monitor and audit dependency installations

Implement logging and monitoring for all dependency installations in CI/CD pipelines. Use tools like Socket Security, Snyk, or GitHub Dependabot to scan for suspicious packages, unexpected version changes, or packages from unknown sources. Set up alerts for any dependencies that are resolved from public registries when private alternatives should exist. Regularly review audit logs to detect potential compromise attempts.

Restrict network access from build environments

Configure build and CI/CD systems to only allow outbound connections to approved package registries. Use firewall rules or network policies to prevent build systems from accessing public registries directly, forcing all dependency downloads through your controlled private mirror. This prevents package managers from bypassing your security controls and downloading packages directly from public sources.

Keep package names confidential and use unpredictable naming

Avoid exposing internal package names in public repositories, build logs, or error messages. When possible, use unpredictable or obfuscated package names for internal dependencies to make it harder for attackers to guess and claim them on public registries. Implement processes to scrub dependency information from any artifacts or documentation that are made public. However, this should be considered defense-in-depth, not a primary security control.

Real-World Examples

2021

Alex Birsan's Research — Apple, Microsoft, PayPal

Security researcher Alex Birsan demonstrated the widespread nature of Dependency Confusion vulnerabilities by successfully compromising build systems at over 35 major companies including Apple, Microsoft, PayPal, Tesla, Uber, and Netflix. By identifying internal package names and publishing malicious packages with inflated version numbers to public registries, Birsan's proof-of-concept code was automatically downloaded and executed on these companies' internal systems. He earned over $130,000 in bug bounties and brought widespread attention to this attack vector.

2021

ua-parser-js Compromise

The popular npm package ua-parser-js, which receives over 8 million weekly downloads, was compromised when attackers gained access to the maintainer's npm account. The attackers published malicious versions (0.7.29, 0.8.0, 1.0.0) containing cryptomining and password-stealing malware. The compromised package affected major companies and millions of users who automatically downloaded the malicious versions through their dependency management systems before the attack was detected and the malicious versions were removed.

2018

event-stream Bitcoin Wallet Theft

The event-stream npm package, downloaded over 2 million times per week, was compromised when the original maintainer transferred ownership to a malicious actor. The new maintainer added a dependency on a malicious package called flatmap-stream, which contained code specifically designed to steal Bitcoin wallets from users of the Copay cryptocurrency application. The attack went undetected for several months because the malicious code was heavily obfuscated and only targeted a specific application.

2022

colors and faker Sabotage

The maintainer of the widely-used npm packages colors and faker intentionally sabotaged their own packages by pushing updates that caused infinite loops and printed gibberish to the console. While this was an act of protest rather than a malicious attack, it demonstrated the trust placed in package maintainers and the potential impact of compromised dependencies. The incident affected thousands of applications that depended on these packages, causing widespread build failures and highlighting the fragility of the npm supply chain.

Ready to Test Your Knowledge?

Put what you have learned into practice. Try identifying and fixing Dependency Confusion vulnerabilities in our interactive coding challenges, or explore more security guides to deepen your understanding.