Secure Python dependency management means installing only exact, pinned versions whose hashes you recorded, from a single trusted index, in an isolated environment. Pip supports this directly through hash-checking mode, and then updates arrive by a reviewed change, not by whatever resolves on build day.
What does pip give you?
The pip documentation on secure installs describes several controls. Hash-checking mode uses local hashes embedded in a requirements file to protect against remote tampering and network issues. Each requirement carries a hash option. It is all or nothing: giving a hash to any requirement turns the mode on for the whole install, which then requires every requirement to be pinned and hashed.
The same page lists two related switches: disallowing source distributions so only prebuilt wheels install, and requiring hashes outright. Source distributions run build scripts at install time, so excluding them removes a place where arbitrary code can execute.
Why do hashes matter?
A version number names a release. A hash names the exact file. If a release on the index is replaced, or an intermediate server serves something different, the hash fails and the install stops. This is the same property a lockfile gives in other ecosystems.
It does not help if the first file you hashed was malicious. That is why you review a dependency before first adding it, and why you keep the count small.
A constructed example
A company's service installs from a requirements file with loose ranges. A maintainer account of a small utility library is phished, and a release adds code that runs at import time. The next build resolves the new version and ships it. Under hash-checking with a pinned file, the build installs only the files recorded earlier, and the update shows up as a proposed change to the file that a reviewer reads.
What else belongs in the setup?
- One index. If you use a private index alongside the public one, a package name that exists on both can resolve to the wrong place. Configure the tool so internal names only come from the internal index.
- Isolation. Use a virtual environment per project, or a container, so installs do not leak across projects.
- Separate dev and production sets. Test tools should not ship.
- Regular updates. Pinning freezes vulnerabilities too, so schedule updates and scan for advisories.
How do you handle updates without chaos?
Pinning only works if updates are routine. Pick a cadence, such as weekly, and have a bot or a script open a pull request that regenerates the pinned file. The reviewer reads the diff, runs the tests, and merges. Security fixes get an expedited path. When updates are boring, nobody is tempted to loosen the pins to avoid the work, and a bad release is caught as a diff instead of as an incident.
How to check or apply this
- Generate a fully pinned requirements file with hashes from your resolver.
- Install in CI with hash checking on and, where possible, wheels only.
- Commit the file and require review for changes to it.
- Pin transitive dependencies, not only the ones you import.
- Decide how internal package names are resolved, and test it with a deliberately conflicting name.
- Scan the pinned set against vulnerability data on every build and on a schedule.
- Generate an SBOM from the installed environment and store it with the release.
Safeguard's dependency scanning and policy gates can enforce steps 6 and 7 in a pipeline, and its Gold free scan at gold.safeguard.sh/scan covers up to 100 dependencies.
Quick check
If I give one requirement a hash, what happens to the rest?
Pip documents hash-checking as all or nothing. Once any requirement has a hash, the mode applies globally and every requirement must be pinned and hashed.
Why exclude source distributions?
They run build code during installation. Wheels do not, so allowing only wheels removes that execution point.
Sources
Last reviewed: 18 October 2026.