If you follow security news in Python, you’ve probably heard about the recent attacks that compromised several popular libraries on PyPI. Legitimate packages were hijacked, malicious versions were published, and developers around the world installed compromised code without even realizing it.
The scariest part? This can happen to any of us, at any time.
What’s Happening?
In recent months, we’ve seen an alarming increase in “supply chain” attacks in the Python ecosystem:
- Typosquatting: Malicious packages with names similar to popular libraries (e.g.,
requetsinstead ofrequests) - Account Takeover: Maintainer accounts being compromised and malicious versions published
- Dependency Confusion: Internal packages being replaced by malicious public versions
- Malicious Updates: Legitimate updates that introduce malicious code
The impact of these attacks can be devastating: credential theft, data exfiltration, backdoors in production, and much more.
The First Line of Defense: Don’t Install “Bleeding Edge” Packages
Here’s an uncomfortable truth: most supply chain attacks are discovered within the first hours or days after publication. If you install a package as soon as it’s released, you’re putting yourself at unnecessary risk.
The solution? Wait a few days before installing new versions.
UV: The Modern Package Manager with Built-in Security
If you don’t know uv yet, it’s the fastest and most modern Python package manager available today. And it has a killer feature for security: --exclude-newer.
How It Works
uv lets you exclude packages published less than X time ago. For example, you can configure it to only install packages that have existed for at least a week:
|
|
This means any malicious package published recently will be automatically ignored.
User/System-Wide Configuration
You can set this globally in the uv config file. On Linux/Mac, add to ~/.config/uv/uv.toml:
|
|
On Windows: %APPDATA%\uv\uv.toml
Per-Project Configuration
Even better, add it to your project’s pyproject.toml so that everyone on the team uses the same policy:
|
|
What If I Need an Urgent Update?
Great question! Imagine a critical CVE is discovered in requests and a fix is released today. You can’t wait a week.
For these cases, you can define exceptions for specific packages:
|
|
This allows you to install specific versions when needed while maintaining general protection.
Why This Works
Think about it: when an attacker compromises a package, they want immediate results. They won’t wait a week to reap the rewards. With exclude-newer:
- ✅ You give the community time to identify malicious packages
- ✅ Security scanners have time to process new releases
- ✅ Maintainers have time to yank problematic versions
- ✅ You drastically reduce your attack surface
It’s like letting wine age — but for dependencies.
Getting Started with UV
If you don’t have uv installed yet:
|
|
Create your project:
|
|
Done! Your project is now automatically protected against bleeding edge packages.
Integrity Verification: Hashing
In addition to avoiding very new packages, another effective technique is using hash verification for your dependencies.
How It Works
When you install a Python package, pip downloads a .whl or .tar.gz file from a repository. Each file has a unique hash (SHA256) that serves as a “fingerprint” of that specific package.
The idea is simple: if you specify the exact hash of the package you want to install, pip will verify that the downloaded file matches that hash. If someone tries to replace the package with a malicious version, the hash won’t match, and the installation will fail.
In Practice
Let’s see how to implement this in your project.
1. Generate the hash file
First, create a requirements.txt file with your dependencies and pinned versions:
|
|
Now, use the pip hash command to generate the hashes:
|
|
Or, more simply, use pip-compile from pip-tools:
|
|
This will generate a requirements.txt file with hashes:
|
|
2. Install with hash verification
Now, when you install your dependencies:
|
|
pip will only install if the hashes match. If someone tries to send you a malicious package, the installation will fail immediately.
Other Layers of Protection
Hash verification is powerful, but it’s not the only defense. Here are other techniques you should use:
1. Pip Audit
Use the pip-audit tool to check for known vulnerabilities:
|
|
This will scan all your dependencies against the PyPI vulnerability database.
2. Dependabot / Renovate
Set up automatic dependency update tools in your repository. They monitor for vulnerabilities and automatically create PRs:
|
|
3. Poetry with Lock File
If you use Poetry, poetry.lock already includes hashes by default:
|
|
This ensures exact versions (with verified hashes) are installed.
4. Isolated Environments
Always use virtual environments. If a malicious dependency is installed, it will be isolated to that specific project:
|
|
5. Private PyPI Index
For critical projects, consider using a private PyPI index (like Artifactory or DevPI):
|
|
This lets you control exactly which packages are available.
6. Dependency Code Review
Before adding a new dependency:
- ✅ Check popularity (downloads, GitHub stars)
- ✅ Check update frequency
- ✅ Read the source code (yes, really)
- ✅ Verify the maintainers are trustworthy
- ✅ Look for open security issues
7. Minimize Dependencies
The best dependency is the one you don’t have.
Always ask yourself: “Do I really need this 500MB library to do something I can implement in 50 lines?”
Conclusion
Supply chain attacks are a real and growing threat in the Python ecosystem. This isn’t paranoia, it’s precaution.
The good news? The tools to protect yourself already exist and are free. The bad news? Most developers don’t use them.
Don’t be part of the statistic. Implement these practices today:
- Migrate to
uvand configureexclude-newer— instant protection - Add hash verification — use
pip-toolsor Poetry - Set up
pip-auditin CI/CD - Enable Dependabot or Renovate
- Educate your team
Security is not optional. It’s responsibility.
Pro tip: Combine uv + hash verification + pip-audit. This trio creates an in-depth defense that is practically impenetrable against supply chain attacks.
References: