Safeguard
Security Guides•

Does the hash PyPI shows protect you from a tampered package?

The sha256 in a PyPI download link guards against corruption, not tampering. Here is why pip does not count it as a local hash and what to do instead.

Safeguard Security Team
4 min read

Not against tampering. PyPI puts a hash in each download link, and pip checks it to catch corrupted downloads. Because the hash comes from the same remote source as the file, it cannot prove the file is the one you reviewed. Pip's hash-checking mode therefore requires hashes you recorded yourself, in your requirements file.

Two hashes that look the same

Every file on a package index has a hash, and it is easy to assume that makes every install verified. There are actually two different checks.

Index-supplied hashLocal hash in requirements
Where it comes fromThe download link fragment, from the indexYour requirements file
Who controls itWhoever controls the index responseYou, via version control
What it catchesCorrupted or truncated downloadsA file that differs from what you approved
Counts toward hash-checking modeNoYes

The pip documentation states this directly. PyPI and certain other servers provide a hash in the fragment of each download URL, and pip checks it as protection against download corruption. Since this hash originates remotely, the guide says, it is not a useful guard against tampering, and it does not satisfy the demand that every package have a local hash.

Why a remote hash cannot be the control

Think about what an attacker needs. If someone could replace a file on an index, they could also update the link that carries its hash. Both come from the same place, so they agree, and the check passes.

A local hash breaks that. You record the digest when you review a release. At install time, pip compares the downloaded file with your recorded digest. A replaced file no longer matches, and the install stops.

The pip guide describes what this protects against: a compromise of the index or of the HTTPS certificate chain, and a package changing without its version number changing on indexes that allow that.

What local hashes do not do

Be honest about the limits.

  • They do not tell you a release is safe. If you hash a malicious version on day one, pip will faithfully install it.
  • They only cover what is listed. In hash-checking mode, pip requires hashes for all requirements and all dependencies, and requires pins.
  • They add upkeep. Every upgrade means new hashes, and a package with wheels for several platforms needs a hash for each file you allow.

So the order of work is: decide the version is acceptable, then record its hash. See how to pin requirements with hashes step by step and generate hashes for a requirements file.

A quick way to tell what you have

Look at your requirements file. If lines carry sha256 values that you or a tool wrote, you have local hashes. If you only see names and versions, you are relying on the index. To bootstrap, run an install with hash enforcement turned on and read the hashes pip reports, then review them before committing.

python -m pip install --require-hashes -r requirements.txt

Safeguard's dependency scanning and SBOM generation record what you actually resolved, which gives reviewers something concrete to compare when a hash changes. A free scan is available at gold.safeguard.sh/scan.

Quick check

Does pip verify the hash in the PyPI link?

Yes, as a corruption check. It does not count as a local hash for hash-checking mode.

Is a hashed requirements file proof the package is safe?

No. It proves the file matches what you recorded. You still need to review the release you recorded.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.