Passlib
Passlib is a comprehensive password hashing framework for Python, supporting over 30 hashing schemes. The current stable version is 1.7.4, which is the last series to support Python 2.x. Future versions (Passlib 1.8+) will require Python 3.5 or newer. It provides cross-platform implementations and a robust framework for managing password hashes in applications.
Warnings
- breaking Passlib 1.7.x is the last series to support Python 2.x, 3.3, and 3.4. Passlib 1.8 and later will require Python >= 3.5.
- breaking The `crypt` module will be removed in Python 3.13. While Passlib 1.7.4 includes fallbacks, there are concerns about its long-term maintenance and full compatibility with Python 3.13 without active updates to address this.
- gotcha Passlib has known compatibility issues with `bcrypt` versions 4.1.0 and above. When using the `bcrypt` scheme, Passlib may encounter an `AttributeError` due to changes in the `bcrypt` module.
- gotcha Direct string comparison (e.g., `hashed_password == other_hash`) to verify passwords is insecure and vulnerable to timing attacks.
- gotcha Calling `PasswordHash.hash()` or `CryptContext.hash()` multiple times with the same plaintext password will produce different hash strings each time (for salted schemes). This is expected behavior as a new random salt is generated with each call.
Install
-
pip install passlib
Imports
- CryptContext
from passlib.context import CryptContext
- pbkdf2_sha256
from passlib.hash import pbkdf2_sha256
Quickstart
from passlib.context import CryptContext
# For new applications, it's recommended to use CryptContext
# schemes=['bcrypt'] or schemes=['argon2'] are good modern choices.
# 'deprecated="auto"' ensures older hashes are automatically marked for upgrade.
pwd_context = CryptContext(
schemes=["pbkdf2_sha256", "bcrypt"],
deprecated="auto",
# Optionally, configure default rounds for schemes.
# Adjust these values based on current security recommendations and desired CPU cost.
pbkdf2_sha256__rounds=600000,
bcrypt__rounds=12,
)
password = "supersecretpassword"
# Hash the password
hashed_password = pwd_context.hash(password)
print(f"Hashed password: {hashed_password}")
# Verify the password
is_valid = pwd_context.verify(password, hashed_password)
print(f"Password valid: {is_valid}")
# Verify with wrong password
is_invalid = pwd_context.verify("wrongpassword", hashed_password)
print(f"Wrong password valid: {is_invalid}")
# Check if hash needs to be upgraded (e.g., if a deprecated scheme was used or rounds are too low)
needs_upgrade = pwd_context.needs_update(hashed_password)
print(f"Hash needs upgrade: {needs_upgrade}")
if needs_upgrade:
print("Upgrading hash...")
new_hashed_password = pwd_context.hash(password)
print(f"New Hashed password after upgrade: {new_hashed_password}")