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.
Common errors
-
ModuleNotFoundError: No module named 'passlib'
cause The 'passlib' library is not installed in the Python environment being used, or the Python interpreter cannot find it due to environment path issues or virtual environment misconfiguration.fixEnsure 'passlib' is installed in your active Python environment using pip: `pip install passlib` or `pip3 install passlib`. If using a virtual environment, activate it before installing. -
TypeError: Unicode-objects must be encoded before hashing
cause This error occurs when attempting to hash a Python Unicode string directly with 'passlib' (or its underlying hashing backends like bcrypt) without first encoding it into bytes.fixEncode the password string to bytes (e.g., UTF-8) before passing it to the hashing function: `password.encode('utf-8')`. -
ValueError: password cannot be longer than 72 bytes
cause The bcrypt hashing algorithm, often used via 'passlib', has a hard limit of 72 bytes for passwords. This error indicates the provided password exceeds this length. This can also be caused by compatibility issues with newer `bcrypt` versions (e.g., 5.0.0) where internal behavior changes caused problems even with shorter inputs.fixEither ensure passwords do not exceed 72 bytes (e.g., `password[:72]`) or, if the issue is a `bcrypt` version conflict, pin `bcrypt` to an earlier compatible version like 4.3.0 in your `requirements.txt`: `bcrypt==4.3.0`. -
AttributeError: module 'bcrypt' has no attribute 'about'
cause This error typically arises from an incompatibility between the installed 'passlib' version and a newer version of the 'bcrypt' backend library, where 'bcrypt' removed the `__about__` attribute that 'passlib' was attempting to access.fixDowngrade the 'bcrypt' library to a version compatible with 'passlib' 1.7.4, for example: `pip install "bcrypt==4.0.1"` or `pip install "bcrypt<4.1.0"`. -
ValueError: Not a valid password hash
cause This error occurs when `passlib` tries to parse or verify a string that does not conform to a valid hash format for the expected scheme.fixEnsure the hash string passed to methods like `hash.verify()` or `hash.identify()` is a valid, correctly formatted hash previously generated by `passlib` or a compatible system.
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}")