Passlib

raw JSON →
1.7.4 verified Tue May 12 auth: no python install: verified

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.

pip install passlib
error 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.
fix
Ensure '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.
error 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.
fix
Encode the password string to bytes (e.g., UTF-8) before passing it to the hashing function: password.encode('utf-8').
error 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.
fix
Either 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.
error 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.
fix
Downgrade 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".
error 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.
fix
Ensure 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.
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.
fix Ensure your project uses Python 3.5+ before upgrading to Passlib 1.8. Consider migrating Python 2.x code if still in use.
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.
fix Monitor Passlib's official channels for updates regarding Python 3.13 compatibility. Consider alternatives like `pwdlib` if issues arise or maintenance remains stalled.
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.
fix Downgrade the `bcrypt` package to version `4.0.1` or lower (`pip install bcrypt==4.0.1`) if you rely on Passlib's `bcrypt` scheme.
gotcha Direct string comparison (e.g., `hashed_password == other_hash`) to verify passwords is insecure and vulnerable to timing attacks.
fix Always use the `.verify()` method provided by `Passlib` (e.g., `pwd_context.verify(password, hashed_hash)`) which uses constant-time comparison.
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.
fix Understand that different hash outputs for the same password are a security feature, not a bug. Store only one hash per password and use `.verify()` for checks.
python os / libc status wheel install import disk
3.10 alpine (musl) wheel - 0.07s 21.3M
3.10 alpine (musl) - - 0.07s 21.3M
3.10 slim (glibc) wheel 1.6s 0.05s 22M
3.10 slim (glibc) - - 0.05s 22M
3.11 alpine (musl) wheel - 0.12s 23.9M
3.11 alpine (musl) - - 0.13s 23.9M
3.11 slim (glibc) wheel 1.7s 0.10s 24M
3.11 slim (glibc) - - 0.10s 24M
3.12 alpine (musl) wheel - 0.10s 15.6M
3.12 alpine (musl) - - 0.12s 15.6M
3.12 slim (glibc) wheel 1.6s 0.10s 16M
3.12 slim (glibc) - - 0.11s 16M
3.13 alpine (musl) wheel - 0.09s 15.3M
3.13 alpine (musl) - - 0.10s 15.2M
3.13 slim (glibc) wheel 1.7s 0.10s 16M
3.13 slim (glibc) - - 0.09s 16M
3.9 alpine (musl) wheel - 0.06s 20.8M
3.9 alpine (musl) - - 0.07s 20.8M
3.9 slim (glibc) wheel 1.9s 0.06s 21M
3.9 slim (glibc) - - 0.07s 21M

This quickstart demonstrates how to use `CryptContext` to hash and verify passwords. `CryptContext` provides a flexible way to manage various hashing schemes, including automatic deprecation and upgrade detection, which is crucial for long-term password security. Modern schemes like `pbkdf2_sha256`, `bcrypt`, and `argon2` are recommended.

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}")