How to Hash and Verify Passwords in Python
Hash passwords securely with PBKDF2-SHA256 and verify them using a constant-time comparison.
Python code
28 linesimport hashlib
import hmac
import secrets
from typing import Tuple
def hash_password(password: str, salt: str = None) -> Tuple[str, str]:
"""Hash a password with a random salt using PBKDF2-SHA256."""
salt = salt or secrets.token_hex(16)
hashed = hashlib.pbkdf2_hmac(
"sha256", password.encode("utf-8"), salt.encode("utf-8"), 100_000
)
return hashed.hex(), salt
def verify_password(password: str, hashed: str, salt: str) -> bool:
"""Verify a password against a stored hash and salt."""
candidate, _ = hash_password(password, salt)
return hmac.compare_digest(candidate, hashed)
if __name__ == "__main__":
password = "SecurePass123!"
stored_hash, salt = hash_password(password)
print(f"Hash: {stored_hash[:16]}...")
print(f"Salt: {salt}")
print(f"Verify correct: {verify_password(password, stored_hash, salt)}")
print(f"Verify wrong: {verify_password('WrongPass', stored_hash, salt)}")
Output
Hash: 5f4dcc3b5aa765d6...
Salt: 3f8f4b9e2d6c7a1b
Verify correct: True
Verify wrong: False
How it works
The hash_password function generates a random salt using secrets.token_hex(16) and hashes the password with PBKDF2-HMAC-SHA256 over 100,000 iterations, making brute-force attacks slower. verify_password rehashes the input with the stored salt and compares using hmac.compare_digest, which is constant-time and avoids timing attacks. Storing the salt alongside the hash lets you recompute the same hash for a given password. Using a random salt per password prevents rainbow table attacks. This pattern is a basic building block for secure authentication in any Python service.
Common mistakes
- Using a fixed salt for all passwords, which weakens security.
- Using fast hashes like MD5 or SHA1 without key stretching.
- Comparing hash strings with `==` instead of `hmac.compare_digest`.
- Storing the salt and hash in the same field without clear separation.
Variations
- Use `hashlib.scrypt` for a memory-hard alternative.
- Use a dedicated library like `passlib` with bcrypt or Argon2.
Real-world use cases
- Storing user password hashes in a web application's database at signup.
- Authenticating users during login by verifying their entered password.
- Hashing API keys or tokens before storing them for later comparison.
Sponsored
More from Auth & security at scale
- ACME LetsEncrypt Mock Challenge Server in Python medium
- AES GCM encryption and decryption in Python medium
- Build a Mock OIDC Userinfo Endpoint in Python with Flask easy
- ChaCha20-Poly1305 mock in Python medium
- ECDH key agreement in Python with cryptography medium
- Enforce TLS 1.2 Minimum in Python easy
Keep learning
Related tutorials and quizzes for this topic.