Hashing Data with hashlib
A cryptographic hash digests arbitrary-length input into a fixed-length fingerprint. The properties that make it useful: the same input always gives the same output, a one-byte change changes the output unpredictably, and finding two different inputs with the same hash (a collision) is computationally hard. Hashes give you integrity and identity without secrecy - anyone can compute and compare the fingerprint, but nobody can recover the original.
Basic Digests
import hashlib
data = b"the quick brown fox"
print(hashlib.sha256(data).hexdigest())
# d7a8fbb307d7809469ca9abcb0082e4f8d5658e564eb45f92bb820ec586435b5
print(hashlib.sha512(data).hexdigest()[:32])
print(hashlib.md5(data).hexdigest()) # fine for checksums, not for security
For anything security-relevant use sha256 (or sha512 for larger digests). MD5 and SHA-1 have known collision attacks and should be treated as broken for authentication - they remain acceptable only for non-adversarial checksums like file-transfer verification.
Incremental Hashing
Large files should be hashed in chunks, not read into memory:
import hashlib
def file_hash(path, algo="sha256", chunk=1 << 20):
h = hashlib.new(algo)
with open(path, "rb") as f:
for block in iter(lambda: f.read(chunk), b""):
h.update(block)
return h.hexdigest()
print(file_hash("/etc/hostname"))
The same pattern works for any stream - an HTTP body, a socket, a video file. hashlib.new(name) accepts any algorithm the platform supports; hashlib.algorithms_guaranteed lists the ones that are always available.
Salted Hashes for User Data
Hashing the same value many times is exploitable: an attacker who guesses "password123" can precompute its hash (a rainbow table). A random per-record salt makes each hash unique, defeating precomputation:
import hashlib
import secrets
salt = secrets.token_bytes(16)
digest = hashlib.sha256(salt + b"password123").hexdigest()
stored = f"{salt.hex()}:{digest}"
This is the idea behind password storage, but for passwords you want a deliberately slow hash - the next chapters cover PBKDF2 and Argon2, which are salted hashes with a work factor, exactly for that purpose.
Hash Is Not Encryption
A digest has no inverse: you cannot "decrypt" a SHA-256 value. If you need to read the original back, you need encryption, not a hash. Use hashes to verify, compare, and index; use ciphers to hide.
Summary
Use SHA-256 for general digests, hash large inputs in chunks, add a random salt whenever the same input recurs (especially user-chosen values), and remember that a hash proves "this exact data" but never reveals it - for that you need real encryption.