"""
Password hashing utilities using bcrypt.
This module provides secure password hashing and verification using the bcrypt
algorithm via the passlib library. Bcrypt is specifically designed for password
hashing and includes several security features:
1. **Salting**: Automatically generates a unique salt for each password to
prevent rainbow table attacks.
2. **Adaptive Hashing**: Intentionally slow (~100ms per hash) to make brute
force attacks computationally expensive. The cost factor can be increased
over time as hardware improves.
3. **One-Way Function**: Bcrypt hashes cannot be reversed to obtain the
original password - only verification is possible.
Security Notes:
- Never store passwords in plain text
- Never log passwords or password hashes
- Always use secure connections (HTTPS) when transmitting passwords
- Consider rate limiting login attempts to prevent brute force attacks
"""
from passlib.context import CryptContext
# ============================================================================
# PASSWORD CONTEXT CONFIGURATION
# ============================================================================
# Create password context with bcrypt as the hashing scheme
# The "deprecated=auto" parameter allows passlib to automatically handle
# hash upgrades if we ever need to migrate to a stronger algorithm in the future
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
[docs]
def hash_password(password: str) -> str:
"""
Hash a password using bcrypt with automatic salt generation.
This function takes a plain text password and returns a bcrypt hash string.
The hash includes the salt and cost factor, so it can be verified later
without storing any additional information.
Bcrypt hash format: $2b$<cost>$<22-char-salt><31-char-hash>
Example: $2b$12$xKzN8o5qCqKqV8xKzN8o5.U9vKzN8o5qCqKqV8xKzN8o5qCqKq
Args:
password: Plain text password to hash (any length, but minimum 8
characters is enforced at the API level)
Returns:
Bcrypt hash string including salt and cost factor. This hash is
safe to store in the database and cannot be reversed to obtain
the original password.
Performance:
Hashing is intentionally slow (~100ms) to prevent brute force attacks.
This is a security feature, not a bug.
Example:
>>> hash_password("my_secure_password")
'$2b$12$xKzN8o5qCqKqV8xKzN8o5.U9vKzN8o5qCqKqV8xKzN8o5qCqKq'
"""
return str(pwd_context.hash(password))
[docs]
def verify_password(plain_password: str, hashed_password: str) -> bool:
"""
Verify a plain text password against a bcrypt hash.
This function compares a plain text password to a previously generated
bcrypt hash. It returns True if the password matches, False otherwise.
The verification process extracts the salt and cost factor from the hash
and re-hashes the plain password to compare.
Args:
plain_password: Plain text password to verify (from login attempt)
hashed_password: Bcrypt hash to verify against (from database).
This hash was previously generated by hash_password().
Returns:
True if the plain password matches the hash, False otherwise.
Returns False for invalid hash formats or mismatched passwords.
Performance:
Verification has the same cost as hashing (~100ms) because it must
re-compute the hash to compare. This prevents timing attacks.
Security:
- Constant-time comparison prevents timing attacks
- Never reveals why verification failed (wrong password vs invalid hash)
- Safe to call with user-provided input
Example:
>>> pwd_hash = hash_password("secret123")
>>> verify_password("secret123", pwd_hash)
True
>>> verify_password("wrong", pwd_hash)
False
"""
return bool(pwd_context.verify(plain_password, hashed_password))