Python hashlib: Secure Hashes

Published on: August 19, 2026
Reading time: 5 minutes
Fingerprint scanner representing digest verification with Python hashlib

The Python hashlib module provides a consistent interface to cryptographic hash functions including SHA-256, SHA-512, SHA-3, SHAKE, and BLAKE2. A hash transforms an arbitrary amount of input into a fixed-size digest. Small input changes produce very different results, making hashes useful for file integrity, content identification, cache keys, deduplication, and cryptographic protocols.

Security depends on choosing the correct primitive for the job. SHA-256 can verify a file, but plain SHA-256 is not appropriate for storing passwords. A hash without a secret key does not prove who created a message. This guide covers the main APIs, incremental hashing, large files, safe comparison, SHAKE, BLAKE2, PBKDF2, and scrypt.

How hashing works

A hash function accepts bytes and produces a digest. The same bytes always produce the same result. There is no normal decrypt operation that recovers the original message, but predictable inputs can still be guessed through brute force. One-way behavior therefore does not provide automatic secrecy.

import hashlib

message = "Academify".encode("utf-8")
hash_object = hashlib.sha256(message)

print(hash_object.digest())
print(hash_object.hexdigest())

digest() returns raw bytes. hexdigest() returns hexadecimal text suitable for manifests, databases, and text protocols. The Python binascii guide explains binary and hexadecimal representations in detail.

Choosing an algorithm

SHA-256 is a common interoperable choice for new integrity checks. SHA-512 may be required by a protocol. SHA-3 uses a different construction from SHA-2. BLAKE2 is fast and supports configurable digest lengths and keyed mode. SHAKE produces variable-length output.

MD5 and SHA-1 have known collision weaknesses. They still occur in legacy formats and non-adversarial identifiers, but should not protect signatures, certificates, or attacker-controlled content. The usedforsecurity=False parameter can make an explicitly non-security use available in restricted builds; it does not repair the algorithm.

print(sorted(hashlib.algorithms_guaranteed))
print("sha256" in hashlib.algorithms_available)

algorithms_guaranteed lists portable algorithms. algorithms_available may include additional names supplied by the OpenSSL library linked to the interpreter.

Incremental updates

You do not need to concatenate or load all data into memory. Multiple calls to update() are equivalent to hashing the concatenated bytes.

hasher = hashlib.sha256()
hasher.update(b"part one")
hasher.update(b"part two")
print(hasher.hexdigest())

This pattern is ideal for uploads, sockets, compressed streams, and large files. It follows the same memory-conscious approach described in reading giant files without freezing Python.

Hashing a file with SHA-256

from pathlib import Path
import hashlib

def sha256_file(path: Path, chunk_size: int = 1024 * 1024) -> str:
    hasher = hashlib.sha256()
    with path.open("rb") as file:
        while chunk := file.read(chunk_size):
            hasher.update(chunk)
    return hasher.hexdigest()

print(sha256_file(Path("package.zip")))

Compare the result with a digest obtained through an authenticated channel. If both file and checksum come from the same compromised source, the comparison does not prove authenticity. For higher-risk distribution, use HTTPS and a digital signature.

Compressed files may also contain internal checksums, but those usually detect accidental corruption. The guide to creating ZIP files with Python explains that distinction.

Using file_digest()

Since Python 3.11, hashlib.file_digest() simplifies binary file hashing and may use optimized I/O paths.

with open("package.zip", "rb") as file:
    digest = hashlib.file_digest(file, "sha256")

print(digest.hexdigest())

Assume that the file position and internal state are unknown after the call. In Python 3.14, a non-blocking file raises BlockingIOError; earlier behavior could accidentally add null bytes to the digest.

Comparing digests

A regular comparison is generally sufficient for a public file checksum. When comparing secret authentication values, use hmac.compare_digest(), which reduces timing differences caused by the first mismatching byte.

import hmac

expected = "a" * 64
calculated = sha256_file(Path("package.zip"))

if not hmac.compare_digest(expected, calculated):
    raise ValueError("File digest does not match")

Constant-time comparison cannot fix an unauthenticated expected value or a weak protocol. The source of the expected digest remains part of the security model.

SHAKE variable-length output

shake_128 and shake_256 require an output length when requesting a digest.

identifier = hashlib.shake_256(b"record-123").hexdigest(20)
print(identifier)  # 40 hexadecimal characters

Choose the size based on collision requirements and the threat model. Do not shorten digests arbitrarily when identifiers are derived from attacker-controlled content.

BLAKE2 and domain separation

BLAKE2b is optimized for 64-bit systems and returns between 1 and 64 bytes. BLAKE2s returns up to 32 bytes. The person argument provides personalization, ensuring that the same input used for different purposes produces unrelated digests.

file_hash = hashlib.blake2b(
    b"content",
    digest_size=32,
    person=b"Files-v1",
).hexdigest()

block_hash = hashlib.blake2b(
    b"content",
    digest_size=32,
    person=b"Blocks-v1",
).hexdigest()

assert file_hash != block_hash

BLAKE2 also supports a native keyed mode. It can authenticate short application messages efficiently, although standard HMAC is often preferred when interoperability and conventional security review are priorities.

Why plain SHA-256 is wrong for passwords

General-purpose hashes are intentionally fast. That is excellent for files but allows attackers to test huge password dictionaries quickly. Password storage needs a slow, tunable, salted algorithm such as Argon2id, scrypt, bcrypt, or PBKDF2.

The Python password hashing guide covers the full authentication workflow. Never copy a simple file-hashing example into a password database.

PBKDF2 with a random salt

import os
import hashlib

salt = os.urandom(16)
iterations = 600_000
password = "long password".encode("utf-8")

derived = hashlib.pbkdf2_hmac(
    "sha256",
    password,
    salt,
    iterations,
)

Store the algorithm, iteration count, salt, and derived value. Calibrate the cost on production-class hardware and impose a sensible maximum password length before derivation.

scrypt and memory cost

hashlib.scrypt() adds memory cost, making specialized parallel attacks more expensive.

derived = hashlib.scrypt(
    password,
    salt=salt,
    n=2**14,
    r=8,
    p=1,
    dklen=32,
)

Parameters must be benchmarked and stored with the resulting value. Handle invalid parameters and memory errors explicitly rather than silently lowering the security settings.

Content IDs, caching, and deduplication

Digests are useful as content identifiers and cache keys. Include every option that changes the result, and add a version marker so a future format change does not reuse old entries.

def cache_key(url: str, language: str) -> str:
    canonical = f"v1\n{language}\n{url}".encode("utf-8")
    return hashlib.sha256(canonical).hexdigest()

When hashing API data, define canonical encoding and ordering. The guide to consuming REST APIs in Python provides additional validation and transport practices.

Common mistakes

Frequent errors include passing str without encoding, using MD5 or SHA-1 for security, confusing hashing with encryption, using plain SHA-256 for passwords, comparing authentication tags with ==, trusting a checksum from the same insecure channel, and reading entire files into memory.

Best practices

Use SHA-256 or the modern algorithm required by the protocol. Stream large input. Record the scheme and version with the digest. Separate integrity from authenticity. Use HMAC or signatures when origin matters. Use a salted, slow KDF for passwords. Do not invent custom cryptographic constructions.

Conclusion

hashlib supports basic file digests, SHA-3, SHAKE, BLAKE2, PBKDF2, and scrypt through a compact interface. The API is easy; the security decision is contextual. A checksum can detect a change, an authenticated message needs a key, a password needs expensive derivation, and a trusted download needs an expected digest obtained authentically.

See the official hashlib documentation and NIST FIPS 180-4.

Share:

Facebook
WhatsApp
Twitter
LinkedIn

Article content

    Related articles

    Projected binary code representing Python binascii conversions
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python binascii: Binary and ASCII

    Learn Python binascii to convert hexadecimal, Base64 and quoted-printable, calculate CRC values, and validate binary data safely.

    Ler mais

    Tempo de leitura: 5 minutos
    18/08/2026
    A developer typing code on a laptop with a Python book beside in an office.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python codecs: Master Encodings

    Learn Python codecs to inspect encodings, handle errors and BOMs, process incremental streams, and migrate codecs.open to open.

    Ler mais

    Tempo de leitura: 6 minutos
    18/08/2026
    A person in a hoodie coding on dual monitors, depicting cybersecurity and hacking themes.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python base64: Encode Binary Data

    Learn Python base64 to encode bytes, use URL-safe Base64, validate padding, enforce limits, and distinguish encoding from encryption.

    Ler mais

    Tempo de leitura: 5 minutos
    18/08/2026
    Close-up of a woman gently holding a Burmese python, showcasing exotic pet care.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python urllib.parse: Handle URLs

    Learn Python urllib.parse to split URLs, build queries, encode components, and avoid risks involving urljoin, redirects, logs, and SSRF.

    Ler mais

    Tempo de leitura: 5 minutos
    18/08/2026
    A laptop screen showing a code editor with visible programming code in a dimly lit environment.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python ipaddress: IPv4 and IPv6

    Learn Python ipaddress to validate IPv4 and IPv6, calculate CIDR networks, split subnets, summarize ranges, and build safer access policies.

    Ler mais

    Tempo de leitura: 5 minutos
    18/08/2026
    A vibrant array of colored thread spools neatly organized in rows, perfect for sewing enthusiasts.
    Advanced Python
    Foto de perfil de Leandro Hirt da Academify

    Python queue: Coordinate Threads

    Learn Python queue to coordinate threads with FIFO, priority, backpressure, task tracking, retries, and graceful shutdown.

    Ler mais

    Tempo de leitura: 4 minutos
    17/08/2026