Skillversitas

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.