Create Hashes with Python `hashlib`

Learn how to use Python's hashlib module to create cryptographic hashes for data integrity verification, file checksums, and secure password storage.

7 min read

The hashlib module in the Python standard library computes cryptographic hash digests. A hash function takes input data of any size and produces a fixed-size output called a digest. The same input always produces the same digest, but even a tiny change in the input produces a completely different digest.

Use hashes to verify file integrity, detect duplicate data, and create unique keys.

pythonpython
import hashlib
 
message = b"Hello, Python"
digest = hashlib.sha256(message).hexdigest()
print(digest)  # 13796e71cca404af8be4e1a094de45557bed2e91bf4cd54056056bc83746186c

hashlib.sha256() creates a SHA-256 hash object. Passing the data directly to the constructor or calling .hexdigest() returns the hash as a 64-character hexadecimal string.

Hashing strings and bytes

Most real text starts out as a string, not raw bytes, so you need one extra step before hashing it. Hash functions operate on bytes, not strings, so encode strings before hashing.

pythonpython
import hashlib
 
text = "hello"
encoded = text.encode("utf-8")
digest = hashlib.sha256(encoded).hexdigest()
print(digest)  # 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824

text.encode("utf-8") converts the string to bytes. hexdigest() returns the hash as a 64-character hex string. The same input always produces the same output.

Available hash algorithms

hashlib supports many algorithms. Use hashlib.algorithms_available to see what your Python installation provides.

pythonpython
import hashlib
 
print(sorted(hashlib.algorithms_available)[:10])

The exact list depends on the OpenSSL build your Python installation links against, but a typical result looks like this:

texttext
['blake2b', 'blake2s', 'md5', 'md5-sha1', 'sha1', 'sha224', 'sha256', 'sha384', 'sha3_224', 'sha3_256']
AlgorithmDigest sizeUse case
SHA-25632 bytesGeneral-purpose hashing (recommended)
SHA-38448 bytesHigher security margin
SHA-51264 bytesMaximum SHA-2 security
SHA-3-25632 bytesNewer standard, alternative to SHA-2
BLAKE2bConfigurableHigh performance, same security as SHA-3
MD516 bytesLegacy checksums only; broken for security
SHA-120 bytesDeprecated; broken for security

Avoid MD5 and SHA-1 for any security-sensitive code. They are vulnerable to collision attacks where two different inputs produce the same hash. SHA-256 and BLAKE2 are safe choices for new code.

Incremental hashing for large data

Hash large files in chunks to avoid loading the entire file into memory.

pythonpython
import hashlib
 
def file_checksum(path, algorithm="sha256"):
    hasher = hashlib.new(algorithm)
    with open(path, "rb") as file:
        while chunk := file.read(8192):
            hasher.update(chunk)
    return hasher.hexdigest()

hasher.update(chunk) feeds each chunk into the hash. The hash accumulates incrementally. After the entire file is processed, hexdigest() returns the final hash.

You can also build a hash incrementally from multiple sources:

pythonpython
import hashlib
 
hasher = hashlib.sha256()
hasher.update(b"Hello, ")
hasher.update(b"world!")
print(hasher.hexdigest())  # 315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3
 
print(hashlib.sha256(b"Hello, world!").hexdigest())  # 315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3

Incremental updates produce the same digest as hashing the concatenated data all at once.

Raw bytes vs hex digest

digest() returns the hash as raw bytes. hexdigest() returns it as a hexadecimal string.

digest()hexdigest()
Return typebytesstr
Length for SHA-25632 bytes64 characters
Best forCompact storage, further crypto operationsDisplay, logging, JSON
pythonpython
import hashlib
 
digest_bytes = hashlib.sha256(b"data").digest()
digest_hex = hashlib.sha256(b"data").hexdigest()
 
print(len(digest_bytes))  # 32
print(digest_hex)             # 3a6eb0790f39ac87c94f3856b2dd2c5d110e6811602261a9a923d3bb23adc8b7

Use digest() when you need compact storage or need to pass the hash to another cryptographic function. Use hexdigest() for display, logging, and human-readable output.

Using BLAKE2 for performance

BLAKE2 is faster than SHA-256 with the same security level. It also supports keyed hashing and variable-length digests.

pythonpython
import hashlib
 
data = b"Large data payload" * 1000
 
h1 = hashlib.blake2b(data).hexdigest()
h2 = hashlib.sha256(data).hexdigest()
 
print(f"BLAKE2b: {h1[:40]}...")
print(f"SHA-256: {h2[:40]}...")

Both lines hash the exact same 19 KB payload, so the difference in the printed prefixes comes only from the algorithm, not the input:

texttext
BLAKE2b: 64c755d5ebdd5a46556337e137fa5eed6d7ee828...
SHA-256: b33259c2f3a9f109bf530bbf413a3c48ab2154a2...

BLAKE2b produces a 64-byte digest by default, but unlike SHA-256, you can configure the output length to any size up to that maximum by passing digest_size:

pythonpython
hasher = hashlib.blake2b(digest_size=16)
hasher.update(b"data")
print(hasher.hexdigest())  # 82f64e6be809763df98195dfa5de656c

This produces a 16-byte (32 hex character) digest. SHA-256 is fixed at 32 bytes.

Password hashing with scrypt

Hash functions like SHA-256 are designed to be fast, which makes them unsuitable for password storage. A fast hash means an attacker can try billions of passwords per second. hashlib.scrypt() is intentionally slow and memory-intensive to resist brute-force attacks.

pythonpython
import hashlib
import os
 
password = b"correct horse battery staple"
salt = os.urandom(16)
 
hashed = hashlib.scrypt(password, salt=salt, n=16384, r=8, p=1)
print(hashed.hex())

scrypt takes a password, a random salt, and cost parameters that control how slow and memory-intensive the computation is. Store the salt and the hash together for later verification. For production password storage, also consider using the bcrypt or argon2 libraries, which provide simpler interfaces with built-in salt generation.

Practical example: verifying downloaded files

A common use of hashing is verifying that a downloaded file has not been corrupted or tampered with.

pythonpython
import hashlib
 
def verify_file(filepath, expected_hash, algorithm="sha256"):
    hasher = hashlib.new(algorithm)
    with open(filepath, "rb") as file:
        while chunk := file.read(8192):
            hasher.update(chunk)
    actual = hasher.hexdigest()
    if actual != expected_hash:
        print(f"Mismatch! Expected: {expected_hash[:16]}..., got: {actual[:16]}...")
        return False
    print("File verified successfully.")
    return True

Calling the function checks a downloaded file against the hash published by the file's distributor, printing a clear success or mismatch message depending on whether the computed digest matches:

pythonpython
verify_file("python-3.14.tar.xz", "d3b5c8...")

The function reads the file in 8 KB chunks, computes the SHA-256 hash, and compares it with the expected value published by the file distributor. A mismatch indicates either corruption or tampering.

Common mistakes

Using MD5 or SHA-1 for security. Both are broken for collision resistance. Use SHA-256 or BLAKE2 for integrity verification and any security-sensitive hashing.

Hashing passwords with SHA-256. SHA-256 is designed to be fast. Password hashing should be slow to resist brute-force attacks. Use hashlib.scrypt(), bcrypt, or argon2 for passwords.

Forgetting to encode strings. Hash functions work on bytes. Always call .encode("utf-8") on strings before hashing, or pass bytes directly.

Comparing hashes with compare_digest when the expected hash is public. secrets.compare_digest prevents timing attacks, but if the expected hash is already known to the attacker (like a published file checksum), there is no secret to protect. Regular == is fine for public integrity checks.

Rune AI

Rune AI

Key Insights

  • Use hashlib.sha256(data).hexdigest() to compute a SHA-256 hash as a hex string.
  • Hash files in chunks with .update() to avoid loading the entire file into memory.
  • Use hashlib.blake2b() for faster hashing with the same security as SHA-256.
  • Do not use SHA-256 or MD5 for password storage; use hashlib.scrypt() or a dedicated library.
  • hashlib.algorithms_available lists every hash algorithm your Python installation supports.
  • The .digest() method returns raw bytes; .hexdigest() returns a hex string.
RunePowered by Rune AI

Frequently Asked Questions

Which hash algorithm should I use?

Use SHA-256 for general-purpose hashing. It is fast, collision-resistant, and widely supported. Use BLAKE2 (via `hashlib.blake2b()`) when you need higher performance with the same security level. Avoid MD5 and SHA-1 for security purposes; they are broken for collision resistance.

Can I use hashlib for password storage?

No. Use a dedicated password hashing library like `bcrypt`, `argon2`, or Python's built-in `hashlib.scrypt()`. These algorithms are intentionally slow and memory-hard, making brute-force attacks impractical. Fast hashes like SHA-256 are designed for speed and are unsuitable for password storage.

How do I hash a large file without loading it into memory?

Read the file in chunks and call `.update(chunk)` on a hash object for each chunk. The hash object accumulates the hash incrementally, so you never hold the entire file in memory.

Conclusion

The hashlib module is the standard way to compute cryptographic hashes in Python. Use SHA-256 for integrity checks and file verification, BLAKE2 for high-performance hashing, and scrypt for password storage. Avoid MD5 and SHA-1 for any new security-sensitive code. Always read large files in chunks when computing hashes.