Résumé
ONYEDIKACHI BERNARDCLOUD · DEVSECOPS · AI
RésuméGet in touch
CASE 23SUPPLY CHAIN

Python Dependency Confusion Defence

A Python build used --extra-index-url, which let pip consider packages from both internal and external repositories. I demonstrated that a higher-versioned external package with the same name as an internal dependency was selected automatically, and identified the controls to prevent dependency confusion.

Status: ASSESSED · VERIFIED

DOMAIN
Supply chain
PLATFORM
Python pip, PyPI, Docker
SEVERITY
Critical
OUTCOME
Dependency confusion path confirmed

TOOLS

PythonpipsetuptoolswheeltwineDockerPyPILinuxcURLVirtual Environment
RESULT
BEFOREMixed index, no pinning
AFTERConfusion path documented
01

Objective

I set out to assess whether the Python dependency management configuration was vulnerable to dependency confusion between internal and external package repositories.

02

Scenario

An application relied on an internal Python package called datadbconnect, hosted on a private PyPI repository. The build configuration used --extra-index-url, which told pip to search both the internal and the public PyPI repository. I wanted to determine whether an attacker who published a higher-versioned package with the same name on the external repository could get it installed instead.

03

What I found

FINDING 01 (CRITICAL): BUILD CONFIGURATION SEARCHED BOTH INTERNAL AND EXTERNAL REPOSITORIES

The pip configuration used --extra-index-url, which added the external PyPI as an additional source alongside the internal repository. Pip selected the highest available version across all configured sources.

pip-configMIXED INDEX
Index configuration:   --extra-index-url (internal + external)
Package resolution:    Highest version wins across all sources
Internal package:      datadbconnect (internal version)

FINDING 02 (CRITICAL): EXTERNAL PACKAGE SELECTED OVER INTERNAL

A package with the same name and a higher version number (9000.0.2) published on the external repository was selected by pip during installation. The package included a post-installation hook that executed code in the build environment.

confusion-resultEXTERNAL SELECTED
Package installed:     datadbconnect-9000.0.2 (external)
Expected package:      datadbconnect (internal)
Execution context:     Build container (root)

The build container ran as root, so the post-installation code had full privileges inside the container.

04

What I did

  1. Identified the internal package name

    The package name datadbconnect was discoverable through a public developer forum post.

  2. Reviewed the pip configuration

    The --extra-index-url setting allowed pip to search both internal and external repositories.

  3. Confirmed the confusion path

    A package with the same name and a higher version on the external repository was selected automatically during installation.

  4. Assessed the impact

    The external package's post-installation hook executed with root privileges inside the Docker build container.

EVIDENCE

TEXT
# Dependency confusion assessment
Internal package:    datadbconnect (internal PyPI)
External package:    datadbconnect-9000.0.2 (external PyPI)
pip behaviour:       Selected higher external version
Execution context:   Docker build container as root
05

Validation

The external package was installed instead of the internal one, and its post-installation code executed inside the build container with root privileges. The dependency confusion path was confirmed end to end.

06

Before and after

CONTROLBEFOREAFTER
Index configuration--extra-index-url (mixed)Risk documented; --index-url recommended
Package pinningNot configuredRecommended: pin versions with hashes
Internal package namespaceNot reserved externallyRecommended: reserve on public PyPI
Build container privilegesRootRecommended: non-root build user
Dependency verificationNoneRecommended: hash verification + lockfile
07

What I recommend

  1. 01Use --index-url (not --extra-index-url) to point pip at a single trusted source, or use a repository manager that proxies and prioritises internal packages.
  2. 02Pin all dependencies to exact versions with hash verification in a lockfile.
  3. 03Reserve internal package names on public PyPI to prevent name squatting.
  4. 04Run build containers as a non-root user to limit the impact of malicious post-installation hooks.
  5. 05Scan dependencies for unexpected version changes or new packages matching internal names.

“Dependency managers must be configured to trust the correct package source and verify dependencies because an attacker can publish a higher-version package with the same name and turn the build process into a code-execution path.”

WHAT I LEARNED

SKILLS IT PROVES

  • Software Supply Chain Security
  • Dependency Management
  • DevSecOps
  • CI/CD Security
  • Vulnerability Assessment
  • Secure Configuration Management
  • Python Security
  • Container Security
  • Package Management
  • Access Control
  • Supply Chain Risk Management
View my labs on GitHub