Add a sha256 hash to every pinned requirement and pip will refuse any download that does not match. That is hash-checking mode. It guards against remote tampering, but it requires exact pins and a hash for every dependency, including the ones pulled in indirectly.
What pip says by default
The pip documentation states that by default pip does no verification of the distributions it runs. Hash-checking mode uses hashes you store locally, attached to each requirement, to guard against remote tampering and network problems. It was added in pip 8.0.
The rules
According to the pip documentation:
- Adding a hash to any requirement switches the mode on for the whole file. You can also force it with the require-hashes option, which is useful in deploy scripts.
- Every requirement and dependency needs a hash. An unhashed dependency is an error, because a partly hashed file could let bad code in through the gap.
- Every requirement must be pinned to an exact version with a double equals sign, or to a URL or path.
- sha256 is recommended. md5, sha1 and sha224 are excluded.
- A package can list several hashes, which helps when it ships wheels for several platforms.
- The sha256 fragments in PyPI links check for corruption only. They do not count as your own recorded hashes.
Steps for an ML project
- Resolve the full dependency tree in a clean environment and freeze exact versions.
- Generate hashes for each file you need. Your lock tool or pip hash can produce them.
- Write them into the requirements file.
- Install with the require-hashes option in CI and in image builds.
- Re-generate on every deliberate upgrade, and review the diff.
numpy==2.1.0 \
--hash=sha256:<hash for linux wheel> \
--hash=sha256:<hash for macos wheel>
pip install --require-hashes -r requirements.txt
Traps specific to ML stacks
| Trap | What to do |
|---|---|
| Many wheels per package for CPU, GPU and OS | Include a hash for each wheel you actually deploy |
| Special index for GPU builds | Use the index URL carefully and see our dependency confusion post |
| Transitive dependencies missing hashes | Generate the file from a full resolve, not by hand |
| Unpinned notebook installs | Move installs into the locked file |
| Source builds that fetch more at build time | Prefer wheels where you can |
The pip documentation also describes an option to install only binary distributions, which fails for packages without one. Some ML teams use it to avoid running build scripts during install, though the failures need handling.
What hashes do not tell you
A hash proves the file matches what you recorded. It does not say the package is free of known vulnerabilities. Pair it with scanning, and review the version you pin.
Where Safeguard fits
Safeguard provides SBOM and dependency scanning, so the exact pinned versions in your lock file can be checked for known vulnerabilities, with reachability analysis to separate what your code calls from what it does not. A free scan is at gold.safeguard.sh/scan.
Quick check
Do I need hashes for transitive dependencies?
Yes. The pip docs say every requirement and dependency needs a hash in this mode, or pip reports an error.
Does hash checking replace vulnerability scanning?
No. It detects tampering after you record a hash. It does not tell you whether a version has known flaws.
Sources
Last reviewed: 19 October 2026.