Mathew K Analytics

Lesson 16 · Python standard library deep dive

Python hashlib, hmac & secrets Explained: Secure Your Code | Standard Library #16

Video sixteen of the twenty-five-part series: hashing, message authentication, and cryptographically secure randomness. hashlib for one-way hashes, hmac for…

⬇ Download notebookOpen in Colab ↗

What you'll learn

Data

No separate download needed — the notebook creates or downloads everything it uses.

📓 Full notebook

Download .ipynb

Python Standard Library Deep-Dive, Video 16: hashlib, hmac, secrets#

  • Video sixteen of the twenty-five-part series: hashing, message authentication, and cryptographically secure randomness.
  • hashlib for one-way hashes, hmac for authenticated hashes, secrets for genuinely secure tokens and passwords.
  • Let's get into it.

Part 1: Why Hashing#

import hashlib
print(hashlib.sha256(b'hello').hexdigest())
print(hashlib.sha256(b'hello').hexdigest())
print(hashlib.sha256(b'Hello').hexdigest())
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969

Part 2: hashlib Basics#

data = b'The quick brown fox'
print(hashlib.md5(data).hexdigest())
print(hashlib.sha1(data).hexdigest())
print(hashlib.sha256(data).hexdigest())
print(hashlib.sha512(data).hexdigest())
print(len(hashlib.sha256(data).digest()))
print(len(hashlib.sha256(data).hexdigest()))
a2004f37730b9445670a738fa0fc9ee5
c519c1a06cdbeb2bc499e22137fb48683858b345
5cac4f980fedc3d3f1f99b4be3472c9b30d56523e632d151237ec9309048bda9
015e6d23e760f612cca616c54f110cb12dd54213f1e046c7607081372402eff4936b379296ed549236020afb37bd3e728a044a4243754f095498c98bc24f77e0
32
64
h = hashlib.sha256()
h.update(b'The quick ')
h.update(b'brown fox')
print(h.hexdigest())
print(h.hexdigest() == hashlib.sha256(b'The quick brown fox').hexdigest())
5cac4f980fedc3d3f1f99b4be3472c9b30d56523e632d151237ec9309048bda9
True

Part 3: Hashing Files#

with open('demo_hash_file.txt', 'w') as f:
    f.write('Sample content for hashing.\n' * 100)
def hash_file(path, algorithm='sha256', chunk_size=8192):
    hasher = hashlib.new(algorithm)
    with open(path, 'rb') as f:
        while chunk := f.read(chunk_size):
            hasher.update(chunk)
    return hasher.hexdigest()
print(hash_file('demo_hash_file.txt'))
bc81d6e9eb8c3e8688760d72bc422efcff68089dbc98a250bab9da8221ff3dd0

Part 4: hashlib.new() and Available Algorithms#

print(sorted(hashlib.algorithms_guaranteed)[:8])
print('sha3_256' in hashlib.algorithms_guaranteed)
print('blake2b' in hashlib.algorithms_guaranteed)
h = hashlib.new('sha3_256', b'quantum-resistant family')
print(h.hexdigest())
['blake2b', 'blake2s', 'md5', 'sha1', 'sha224', 'sha256', 'sha384', 'sha3_224']
True
True
88a37ebe456724d0bc3db00e92d63ab439a9308835b120ecf8ebeb2231a46aef

Part 5: hmac - Message Authentication Codes#

import hmac
secret_key = b'shared-secret-key'
message = b'transfer $100 to account 42'
signature = hmac.new(secret_key, message, hashlib.sha256).hexdigest()
print(signature)
wrong_key = b'wrong-key'
wrong_signature = hmac.new(wrong_key, message, hashlib.sha256).hexdigest()
print(signature == wrong_signature)
0ba9d93223943cfd7f79a26f49db987c9ac1821a27a7beb5627687a126713f1d
False

Part 6: hmac.compare_digest - Timing-Safe Comparison#

received_signature = signature
is_valid = hmac.compare_digest(signature, received_signature)
print(is_valid)
is_valid_tampered = hmac.compare_digest(signature, wrong_signature)
print(is_valid_tampered)
True
False

Part 7: secrets - Secure Tokens#

import secrets
token1 = secrets.token_bytes(16)
print(len(token1))
token2 = secrets.token_hex(16)
print(token2)
print(len(token2))
token3 = secrets.token_urlsafe(16)
print(token3)
16
02637c96e4f991dbcb14f2f8517f76c5
32
TbT-n0qqc-5wDCNVcamzsQ

Part 8: secrets - Secure Choice and Password Generation#

import string
alphabet = string.ascii_letters + string.digits
password = ''.join(secrets.choice(alphabet) for _ in range(12))
print(password)
print(len(password))
roll = secrets.randbelow(6) + 1
print(1 <= roll <= 6)
AxAcVX6V1eNB
12
True

Part 9: Password Hashing Correctly#

password = 'correct horse battery staple'
salt = secrets.token_bytes(16)
hashed = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 200_000)
print(len(hashed))
def verify_password(password, salt, expected_hash):
    candidate = hashlib.pbkdf2_hmac('sha256', password.encode(), salt, 200_000)
    return hmac.compare_digest(candidate, expected_hash)
print(verify_password('correct horse battery staple', salt, hashed))
print(verify_password('wrong guess', salt, hashed))
32
True
False

Part 10: Putting It Together - Webhook Signature Verification#

webhook_secret = secrets.token_bytes(32)
def sign_payload(payload_bytes, secret):
    return hmac.new(secret, payload_bytes, hashlib.sha256).hexdigest()
def verify_webhook(payload_bytes, received_signature, secret):
    expected = sign_payload(payload_bytes, secret)
    return hmac.compare_digest(expected, received_signature)
payload = b'{"event": "payment.success", "amount": 4999}'
signature = sign_payload(payload, webhook_secret)
print(verify_webhook(payload, signature, webhook_secret))
tampered_payload = b'{"event": "payment.success", "amount": 999999}'
print(verify_webhook(tampered_payload, signature, webhook_secret))
True
False

Wrap-Up: What You Learned#

  • hashlib produces deterministic, one-way, fixed-size digests; sha256/sha512 are the modern recommended choices, md5/sha1 are broken.
  • Hash objects can be fed incrementally with update(), enabling memory-safe chunked file hashing.
  • hashlib.new(name) builds a hasher dynamically; algorithms_guaranteed lists every universally available algorithm.
  • hmac combines a secret key with a message, proving authenticity, not just integrity.
  • hmac.compare_digest performs constant-time comparison, avoiding timing side-channel attacks.
  • secrets uses the OS's cryptographically secure random source; never use random for tokens, passwords, or keys.
  • secrets.token_bytes/token_hex/token_urlsafe generate secure tokens; secrets.choice/randbelow drive secure random selection.
  • Real password storage needs a random salt plus a deliberately slow algorithm like pbkdf2_hmac, never a single fast hash pass.
  • A realistic combined pattern: hmac-signing and compare_digest-verifying webhook payloads.
  • That wraps up hashlib, hmac, and secrets. Next up: contextlib, for building and combining context managers.

Found this useful?

All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.