|$ curl https://forge-ai.dev/api/markdown?path=docs/python/hashlib-secrets
$cat docs/hashlib-&-secrets.md
updated Today·18-24 min read·published

hashlib & secrets

PythonSecurityCryptoIntermediate🎯Free Tools
Introduction

hashlib provides cryptographic hashes; hmac provides keyed message authentication; secrets provides secure random tokens. For password hashing, use specialized KDFs (bcrypt/argon2), not raw SHA.

hashlib
hashlib_demo.py
Python
1import hashlib
2from pathlib import Path
3
4print(hashlib.sha256(b"payload").hexdigest())
5print(hashlib.sha512(b"payload").hexdigest())
6print(hashlib.blake2b(b"payload", digest_size=32).hexdigest())
7
8# streaming large files
9h = hashlib.sha256()
10data = b"x" * 10_000
11for i in range(0, len(data), 4096):
12 h.update(data[i:i+4096])
13print(h.hexdigest())
14
15def file_sha256(path: Path) -> str:
16 h = hashlib.sha256()
17 with path.open("rb") as f:
18 for chunk in iter(lambda: f.read(1 << 16), b""):
19 h.update(chunk)
20 return h.hexdigest()
AlgorithmUse
sha256 / sha512Integrity checksums
blake2b / blake2sFast secure hashing
sha3_256SHA-3 family
md5 / sha1Legacy only — not security
HMAC
hmac_demo.py
Python
1import hashlib
2import hmac
3import secrets
4
5key = secrets.token_bytes(32)
6msg = b"body"
7sig = hmac.new(key, msg, hashlib.sha256).hexdigest()
8
9def verify(key: bytes, msg: bytes, sig_hex: str) -> bool:
10 expected = hmac.new(key, msg, hashlib.sha256).hexdigest()
11 return hmac.compare_digest(expected, sig_hex)
12
13assert verify(key, msg, sig)
14assert not verify(key, msg, "00" * 32)

danger

Always compare secrets with hmac.compare_digest — never == on digests.
secrets
secrets_demo.py
Python
1import secrets
2import string
3
4print(secrets.token_bytes(16))
5print(secrets.token_hex(16))
6print(secrets.token_urlsafe(16))
7
8alphabet = string.ascii_letters + string.digits
9password = "".join(secrets.choice(alphabet) for _ in range(20))
10print(password)
11
12# secrets.compare_digest for str secrets too
13a = secrets.token_urlsafe(8)
14assert secrets.compare_digest(a, a)

warning

Never use random.random / random.choices for security tokens.
Password Hashing Overview

Password storage needs slow, memory-hard KDFs with unique salts. Prefer argon2 or bcrypt libraries — not SHA256(password).

passwords.py
Python
1# pip install bcrypt
2import bcrypt
3
4password = b"correct horse battery staple"
5hashed = bcrypt.hashpw(password, bcrypt.gensalt(rounds=12))
6assert bcrypt.checkpw(password, hashed)
7
8# pip install argon2-cffi
9# from argon2 import PasswordHasher
10# ph = PasswordHasher()
11# h = ph.hash("correct horse battery staple")
12# ph.verify(h, "correct horse battery staple")
SchemeNotes
argon2idModern default recommendation
bcryptWidely supported; 72-byte limit
scryptAvailable in hashlib.scrypt
PBKDF2hashlib.pbkdf2_hmac — OK if tuned
Plain SHANever for passwords
stdlib KDFs
kdf.py
Python
1import hashlib
2import secrets
3
4password = b"secret"
5salt = secrets.token_bytes(16)
6dk = hashlib.scrypt(password, salt=salt, n=2**14, r=8, p=1, dklen=32)
7print(dk.hex())
8
9dk2 = hashlib.pbkdf2_hmac("sha256", password, salt, 200_000, dklen=32)
10print(dk2.hex())
API Signing Pattern
sign.py
Python
1import hashlib
2import hmac
3import secrets
4import time
5
6def sign(secret: bytes, body: bytes, ts: int | None = None) -> str:
7 ts = ts or int(time.time())
8 msg = f"{ts}.".encode() + body
9 sig = hmac.new(secret, msg, hashlib.sha256).hexdigest()
10 return f"t={ts},v1={sig}"
11
12def check(secret: bytes, body: bytes, header: str, skew: int = 300) -> bool:
13 parts = dict(p.split("=", 1) for p in header.split(","))
14 ts = int(parts["t"])
15 if abs(int(time.time()) - ts) > skew:
16 return False
17 expected = sign(secret, body, ts)
18 return secrets.compare_digest(expected, f"t={ts},v1={parts['v1']}")
Security Checklist
  • secrets for tokens; hmac.compare_digest for compares
  • SHA-256+ for integrity; never MD5 for security
  • argon2id/bcrypt for passwords
  • Unique salt per password
  • Do not log secrets, tokens, or raw password hashes
File Integrity Manifest
manifest.py
Python
1import hashlib
2import json
3from pathlib import Path
4
5def digest_tree(root: Path) -> dict[str, str]:
6 out: dict[str, str] = {}
7 for p in sorted(root.rglob("*")):
8 if p.is_file():
9 h = hashlib.sha256(p.read_bytes()).hexdigest()
10 out[str(p.relative_to(root))] = h
11 return out
12
13# Path("manifest.json").write_text(json.dumps(digest_tree(Path(".")), indent=2))
URL-safe Tokens for Sessions
tokens.py
Python
1import secrets
2from dataclasses import dataclass
3from datetime import datetime, timedelta, timezone
4
5@dataclass
6class Session:
7 token: str
8 expires_at: datetime
9
10def new_session(ttl_hours: int = 24) -> Session:
11 return Session(
12 token=secrets.token_urlsafe(32),
13 expires_at=datetime.now(timezone.utc) + timedelta(hours=ttl_hours),
14 )
15
16s = new_session()
17print(s.token, s.expires_at.isoformat())
Timing Attacks

String comparison with == short-circuits; attackers measure time. Use hmac.compare_digest / secrets.compare_digest.

timing.py
Python
1import secrets
2user_supplied = "abc"
3expected = "abc"
4print(secrets.compare_digest(user_supplied, expected))
Do Not
  • Roll your own crypto protocols
  • Use MD5/SHA1 for security
  • Hash passwords with single SHA256
  • Put API keys in URLs/query strings if avoidable
  • Log Authorization headers
algorithms_available / guaranteed
algos.py
Python
1import hashlib
2print("sha256" in hashlib.algorithms_guaranteed)
3print(sorted(hashlib.algorithms_available)[:10])
FAQ
QuestionAnswer
Checksum a download?SHA-256 + compare_digest
Session id?secrets.token_urlsafe
Password at rest?argon2id or bcrypt
API request auth?HMAC with shared secret
UUID for secrets?OK-ish; token_urlsafe is fine
Summary

Hash for integrity, HMAC for authenticity, secrets for tokens, and argon2/bcrypt for passwords. Compare digests in constant time.

$Blueprint — Engineering Documentation·Section ID: PYTHON-HASH-SECRETS·Revision: 1.0

Community

Get help on Slack, Discord or VIP

Stuck on a guide? Join the community and ask.