A Python service template should install from a pinned requirements file with hash checking on, and where practical prefer prebuilt wheels over source distributions. pip supports both, and together they make installs repeatable and harder to tamper with.
What pip offers
The pip secure installs documentation describes two switches. Hash checking mode is enabled by passing --require-hashes. Disallowing source distributions is done by passing --only-binary :all:.
Hash checking mode was added in pip 8.0. It uses hashes embedded in a requirements file, one --hash option per requirement, to protect against remote tampering and network issues. The documentation notes that it is all or nothing: if you turn it on, every requirement needs a hash. It can also be forced with --require-hashes, which the docs say is useful in deploy scripts to make sure the author of the requirements file provided hashes, and as a way to bootstrap your list, since it shows hashes of the packages it fetched.
Why prebuilt wheels matter
A source distribution can run build code on the machine that installs it. Refusing source distributions removes that step for the install. The tradeoff is that a few packages only publish source, so you will need a documented exception path.
What the template should include
| File or setting | Purpose |
|---|---|
| requirements.in or equivalent | Human written list of direct dependencies |
| requirements.txt with hashes | Exact resolved versions with hash for each |
| CI install step with --require-hashes | Fails if a file changed |
| --only-binary option where possible | Avoids build time code execution |
| Update automation config | Regenerates the pinned file through pull requests |
| A short README note | How to add a dependency and regenerate hashes |
Use whichever tool your organization has standardized on to compile the pinned file, and keep that choice in one place.
Steps
- Choose how the pinned file is produced and make it one command.
- Generate it in the template's own CI and commit it.
- Set the install step to use hash checking.
- Add the binary only option, and list known exceptions in the template.
- Run a clean container build in CI to prove install works from nothing.
- Add a scan step against the pinned file.
Pitfalls
- Hash checking and unpinned requirements do not mix. pip's all or nothing rule means one unhashed line breaks the install, which is usually the intended signal.
- Hashes cover the files you chose, but multiple platforms need multiple hashes per package. Plan for your CI and production platforms.
- Machine learning projects often pull large packages with native code. See whether pip hash checking can protect an ML environment for that case.
Keep the file alive
A pinned file that never changes becomes a list of known vulnerable versions. Pair it with scheduled update pull requests and a scan after merge.
Where Safeguard fits
Safeguard scans Python dependencies, builds SBOMs, and uses reachability analysis to show which findings your code can actually hit. Policies and gates apply in pull requests. Try a free scan at gold.safeguard.sh/scan.
Try it yourself
Open the demo and compare the full finding list with the reachable subset, which is how you decide what to fix first in a pinned file.
Click through it at your own pace, or open it full screen.
Quick check
Do all requirements need hashes?
Yes, in hash checking mode. The pip docs describe it as all or nothing.
Does hash checking find vulnerabilities?
No. It detects changed files. You still need a vulnerability scan.
Sources
Last reviewed: 19 October 2026.