hashlib provides secure hash algorithms for integrity checking, password storage, and cryptographic operations.

Basic Hashing

import hashlib
 
# Create hash object and update
h = hashlib.sha256()
h.update(b"Hello ")
h.update(b"World")
 
# Get digest
print(h.hexdigest())  # Hex string
print(h.digest())     # Raw bytes
 
# One-liner
hashlib.sha256(b"Hello World").hexdigest()

Available Algorithms

import hashlib
 
# Always available
hashlib.md5(b"data").hexdigest()
hashlib.sha1(b"data").hexdigest()
hashlib.sha256(b"data").hexdigest()
hashlib.sha384(b"data").hexdigest()
hashlib.sha512(b"data").hexdigest()
 
# SHA-3 family (Python 3.6+)
hashlib.sha3_256(b"data").hexdigest()
hashlib.sha3_512(b"data").hexdigest()
hashlib.shake_256(b"data").hexdigest(32)  # Variable length
 
# BLAKE2 (fast, secure)
hashlib.blake2b(b"data").hexdigest()
hashlib.blake2s(b"data").hexdigest()
 
# List all available
print(hashlib.algorithms_available)
print(hashlib.algorithms_guaranteed)

Hashing Files

import hashlib
 
def hash_file(path, algorithm="sha256", chunk_size=8192):
    """Hash a file without loading it entirely into memory."""
    h = hashlib.new(algorithm)
    with open(path, "rb") as f:
        while chunk := f.read(chunk_size):
            h.update(chunk)
    return h.hexdigest()
 
# Usage
file_hash = hash_file("large_file.zip")
print(f"SHA-256: {file_hash}")
 
# Verify download
expected = "a1b2c3d4..."
if hash_file("download.zip") == expected:
    print("Integrity verified")

Hashing Strings

import hashlib
 
# Always encode strings to bytes first
text = "Hello World"
 
# Using encode()
hashlib.sha256(text.encode()).hexdigest()
hashlib.sha256(text.encode("utf-8")).hexdigest()
 
# Using bytes literal
hashlib.sha256(b"Hello World").hexdigest()

Password Hashing

import hashlib
import os
 
# DON'T use plain hashing for passwords
# bad_hash = hashlib.sha256(password.encode()).hexdigest()
 
# DO use PBKDF2 with salt
def hash_password(password: str) -> tuple[bytes, bytes]:
    """Hash password with random salt."""
    salt = os.urandom(32)
    key = hashlib.pbkdf2_hmac(
        "sha256",
        password.encode(),
        salt,
        iterations=100_000
    )
    return salt, key
 
def verify_password(password: str, salt: bytes, key: bytes) -> bool:
    """Verify password against stored hash."""
    new_key = hashlib.pbkdf2_hmac(
        "sha256",
        password.encode(),
        salt,
        iterations=100_000
    )
    return new_key == key
 
# Usage
salt, key = hash_password("secret123")
print(verify_password("secret123", salt, key))  # True
print(verify_password("wrong", salt, key))       # False

HMAC (Hash-Based Message Authentication)

import hashlib
import hmac
 
# Create HMAC
key = b"secret-key"
message = b"Hello World"
 
# Using hmac module (preferred)
h = hmac.new(key, message, hashlib.sha256)
print(h.hexdigest())
 
# Verify HMAC (timing-safe comparison)
received_hmac = "..."
expected_hmac = hmac.new(key, message, hashlib.sha256).hexdigest()
if hmac.compare_digest(received_hmac, expected_hmac):
    print("Message authentic")

Digest Sizes

import hashlib
 
# Digest sizes in bytes
print(hashlib.md5().digest_size)      # 16 (128 bits)
print(hashlib.sha1().digest_size)     # 20 (160 bits)
print(hashlib.sha256().digest_size)   # 32 (256 bits)
print(hashlib.sha512().digest_size)   # 64 (512 bits)
print(hashlib.blake2b().digest_size)  # 64 (512 bits)
print(hashlib.blake2s().digest_size)  # 32 (256 bits)

BLAKE2 (Modern Choice)

import hashlib
 
# BLAKE2b - optimized for 64-bit
h = hashlib.blake2b(b"data", digest_size=32)  # Custom size
print(h.hexdigest())
 
# BLAKE2s - optimized for 32-bit/smaller
h = hashlib.blake2s(b"data")
print(h.hexdigest())
 
# With key (keyed hashing, like HMAC)
h = hashlib.blake2b(b"data", key=b"secret")
print(h.hexdigest())
 
# With personalization
h = hashlib.blake2b(b"data", person=b"myapp")
print(h.hexdigest())

SHAKE (Variable-Length Output)

import hashlib
 
# SHAKE256 - extendable output
h = hashlib.shake_256(b"data")
print(h.hexdigest(32))  # 32 bytes output
print(h.hexdigest(64))  # 64 bytes output
print(h.hexdigest(128)) # 128 bytes output

Common Patterns

import hashlib
import os
 
def content_hash(data: bytes) -> str:
    """Generate content-addressable hash."""
    return hashlib.sha256(data).hexdigest()
 
def generate_token(length: int = 32) -> str:
    """Generate secure random token."""
    return hashlib.sha256(os.urandom(length)).hexdigest()
 
def hash_dict(d: dict) -> str:
    """Hash a dictionary deterministically."""
    import json
    canonical = json.dumps(d, sort_keys=True)
    return hashlib.sha256(canonical.encode()).hexdigest()
 
def checksum(path: str) -> str:
    """Generate file checksum."""
    return hash_file(path, "sha256")
 
def verify_checksum(path: str, expected: str) -> bool:
    """Verify file against expected checksum."""
    return hash_file(path, "sha256") == expected.lower()

API Request Signing

import hashlib
import hmac
import time
 
def sign_request(method: str, path: str, body: str, secret: str) -> str:
    """Sign an API request."""
    timestamp = str(int(time.time()))
    message = f"{method}\n{path}\n{timestamp}\n{body}"
    
    signature = hmac.new(
        secret.encode(),
        message.encode(),
        hashlib.sha256
    ).hexdigest()
    
    return f"{timestamp}:{signature}"
 
def verify_request(
    method: str, path: str, body: str, 
    secret: str, signature: str, max_age: int = 300
) -> bool:
    """Verify signed API request."""
    timestamp_str, received_sig = signature.split(":")
    timestamp = int(timestamp_str)
    
    # Check age
    if abs(time.time() - timestamp) > max_age:
        return False
    
    # Reconstruct and compare
    message = f"{method}\n{path}\n{timestamp_str}\n{body}"
    expected_sig = hmac.new(
        secret.encode(),
        message.encode(),
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(received_sig, expected_sig)

Cache Keys

import hashlib
import json
 
def cache_key(*args, **kwargs) -> str:
    """Generate cache key from arguments."""
    data = json.dumps({"args": args, "kwargs": kwargs}, sort_keys=True)
    return hashlib.sha256(data.encode()).hexdigest()[:16]
 
# Usage
key = cache_key("users", page=1, limit=20)
print(key)  # Short hash for cache lookup

Security Notes

# MD5 and SHA1 are broken for cryptographic purposes
# Use SHA-256 or BLAKE2 for new code
 
# For passwords, use these instead:
# - hashlib.pbkdf2_hmac()
# - argon2 (pip install argon2-cffi)
# - bcrypt (pip install bcrypt)
 
# Always use constant-time comparison
import hmac
hmac.compare_digest(a, b)  # Safe
a == b  # Vulnerable to timing attacks

Algorithm Selection

Use CaseRecommended
General hashingSHA-256, BLAKE2
File checksumsSHA-256
Password hashingPBKDF2, argon2, bcrypt
HMACSHA-256, SHA-512
Speed-criticalBLAKE2
Legacy compatibilitySHA-1 (with caution)

hashlib handles most hashing needs. Use it for integrity checks and HMAC, but reach for specialized libraries for password storage.

React to this post: