skip to content

What is a dependency confusion (substitution) attack in the context of Maven, and why is Maven susceptible to it?

level: middleimportance: must knowfreq 60%

answer

  1. same groupId:artifactId published publicly
  2. build reaches both private + public repo
  3. first repo to answer wins
  4. lock to one virtual repo via mirrorOf *
  5. own your groupId namespace

basics

~20 s

An attacker publishes a package to a public repo with the same groupId/artifactId as your internal one. If your build can reach both repos, it may download the malicious public version instead of the private one.

solid answer

~50 s

Dependency confusion happens when an internal artifact coordinate (groupId:artifactId:version) also exists on a public repository like Maven Central. If a build is configured to consult both an internal repo and a public one, an attacker who publishes a matching (often higher-version) artifact publicly can get their malicious code pulled in. Maven is exposed when repositories are aggregated without isolation: multiple <repositories> entries, a misconfigured proxy that falls through to upstream on a miss, or developers leaving Central enabled alongside the internal Nexus/Artifactory. Unlike npm, Maven version selection is not strictly 'highest wins' for direct deps (the declared version is used), so the classic attack is more about which repository answers first, or transitive mediation pulling a substituted coordinate. Defenses: lock resolution to a single trusted virtual repo via <mirrors>, never publish internal coordinates under a public-namespace groupId, and verify checksums/signatures.

code

xml · 6 lines
xml
<mirror>
  <id>company-virtual</id>
  <url>https://nexus.acme.internal/repository/maven-virtual/</url>
  <!-- Force ALL resolution through one trusted source -->
  <mirrorOf>*</mirrorOf>
</mirror>

go deeper

for a junior

Knows that a malicious public package with the same name can sneak into a build if the build can see both repos.

for a middle

Explains the same-coordinate collision, multiple-repository resolution, and the basic mirror/namespace fixes.

for a senior

Distinguishes Maven's declared-version semantics from npm's highest-wins, reasons about proxy fall-through and transitive mediation.

for a principal

Defines org-wide policy: single virtual repo, owned namespaces, signature/checksum enforcement, and prevents internal coordinates from ever being proxied upstream.

## What the attack is **Dependency confusion** (also called **dependency substitution** or **namespace confusion**) is a supply-chain attack where an attacker tricks your build into downloading a malicious package from a public repository instead of the legitimate one from your private repository. The key ingredients: - Your organization builds an internal library, say `com.acme:billing-core:1.4.0`, and publishes it to a private repository (Nexus, Artifactory, GitHub Packages). - Your `pom.xml` declares a dependency on `com.acme:billing-core`. - Your build is configured to look at **both** the private repo **and** a public repo (Maven Central). - An attacker discovers the coordinate `com.acme:billing-core` (e.g. from a leaked pom, a public CI log, or an inadvertently published artifact) and publishes a malicious artifact with the **same groupId and artifactId** to a public repository they can write to. If the build resolves the public copy first, it executes attacker-controlled code at compile/test/run time. ## Why Maven can be vulnerable Maven resolves an artifact by searching its configured repositories in order and using the **first** repository that has the requested coordinate. Vulnerability arises from configuration, not the algorithm itself: - **Multiple `<repositories>`** declared in the POM or `settings.xml` — if both an internal repo and Central are listed, whichever answers first for a given coordinate wins. - **Pass-through proxies** — a repository manager configured to fall through to an upstream public mirror on a cache miss can fetch the attacker's artifact transparently. - **Shared groupId namespace** — using a groupId you do **not** control on a public registry (e.g. `com.acme` when `acme.com` isn't yours, or a generic prefix) lets an attacker register it publicly. Note a Maven-specific nuance: for **direct** dependencies Maven uses the **declared version**, so the npm-style "attacker publishes 99.0.0 and highest wins" does not directly apply. The Maven attack vector is primarily **which repository serves the coordinate**, plus transitive **version mediation** if a substituted coordinate enters the graph. ## How to prevent it - **Single trusted entry point**: route ALL resolution through one virtual/group repository on your repo manager via `<mirrors>` with `<mirrorOf>*</mirrorOf>`, so the build never talks to Central directly. - **Never use a public-namespace groupId for private artifacts** unless you own that namespace. - **Verify integrity**: enforce checksums (`-C` / `--strict-checksums`) and PGP signatures (`maven-gpg-plugin` to sign, verification on consume). - **Block untrusted fallback**: configure the repo manager so internal coordinate prefixes are never proxied from upstream (exclusion/priority/foreground patterns). ```xml <settings> <mirrors> <mirror> <id>company-virtual</id> <name>Single trusted source</name> <url>https://nexus.acme.internal/repository/maven-virtual/</url> <mirrorOf>*</mirrorOf> </mirror> </mirrors> </settings> ```

  • Does Maven's 'highest version wins' make it as vulnerable as npm?
    No. For direct dependencies Maven uses the declared version, not the highest available. The Maven risk is mainly which repository serves the coordinate and transitive mediation, not automatic version upgrade.
  • How does owning your groupId namespace help?
    If the groupId maps to a domain/namespace only you can publish under on public registries, an attacker cannot register a colliding coordinate there, removing the public collision entirely.

Like mailing a check to 'ACME Billing' when two companies use that name — if the post office routes to whichever address it finds first, an impostor who registered the same name can intercept it.

saying these in an interview costs you the question

  • Claiming Maven auto-picks the highest version for direct deps (it uses the declared version).
  • Saying 'just remove Central from the POM' without realizing transitive deps or mirrors may still reach it.
  • Treating it as purely a version problem rather than a repository-routing/namespace problem.

context