To lock down installs for a Python prototype, pin exact versions in a requirements file, add hashes with pip's hash-checking mode, and consider installing wheels only. Pinning keeps builds repeatable, hashes catch tampering, and binary-only installs avoid running build code from source packages. Apply them as the prototype nears production.
What pip's own guidance says
The pip documentation has a page on secure installs. It describes enabling hash-checking mode with --require-hashes and disallowing source distributions with --only-binary :all: as a way to get a more secure installation mechanism. It says hash-checking mode uses hashes embedded in a requirements file to protect against remote tampering and network issues.
Three layers, from light to strict
| Layer | What you do | What it protects against |
|---|---|---|
| Pin versions | Exact version for every package | Surprise upgrades between builds |
| Hash checking | Add a hash per file and use --require-hashes | A changed or tampered file with the same version |
| Wheels only | Use --only-binary :all: | Code running from a source package during build |
Layer 1: pin versions
AI assistants often write a requirements file with loose ranges or no versions at all. Install into a clean virtual environment, run the tests, and then record exact versions, including indirect packages. A lock file from your preferred tool does the same job. Commit it.
Layer 2: add hashes
In hash-checking mode pip refuses to install a file whose hash is not listed, and the documentation describes that requirement applying to the whole set of packages, so every dependency including indirect ones must have a hash. Tools that compile lock files can generate the hashes for you, which is far easier than typing them.
Hash checking will not tell you whether a package is good. It tells you that the file you install is the file you recorded. Vet the package first, as covered in our five-minute vetting guide at /resources/blog/vet-a-package-an-ai-tool-added-in-five-minutes.
Layer 3: wheels only
Source packages may run build code on your machine at install time. Restricting to prebuilt wheels avoids that. Some packages publish no wheel for your platform, so you may need an exception for a few, which you should review by hand.
When to apply each layer
- Prototype: pin versions.
- Pilot: add hashes in CI builds.
- Production: add wheels-only where feasible, and update through reviewed pull requests.
A short example workflow
Create a clean virtual environment, install the packages your prototype needs, and run the tests. Generate a lock file with hashes using your preferred tool, commit it, and change your CI install step to use hash checking. Then run the build in a fresh container to confirm that nothing depends on your laptop. If a package without a wheel blocks the wheels-only option, note the exception and the reason in the repository.
Keep it fresh
A locked file does not stay safe by itself. Schedule regular updates and scan the lock file for known vulnerabilities, since pinning also pins any flaw in that version.
Where Safeguard fits
Safeguard scans Python dependency files for known vulnerabilities, generates an SBOM and can gate pull requests that add risky packages. Try a free scan at gold.safeguard.sh/scan.
Quick check
Does hash checking replace vulnerability scanning?
No. Hashes prove integrity, not safety. You still need to scan for known vulnerabilities.
Why does hash checking require every package to have a hash?
Because one unchecked package would leave a gap. Lock file tools include indirect packages for this reason.
Sources
Last reviewed: 19 October 2026.