passlib crashes with bcrypt 5.x — use bcrypt directly
passlib crashes with bcrypt 5.x — use bcrypt directly
Severity: P2 (runtime crash on any password-handling endpoint when bcrypt 4.1+ resolves) · Applies to: any service that uses
passlib[bcrypt]Related: ADR-039 — Supabase Auth confirmation and hardening
Problem
Using passlib[bcrypt] for password hashing crashes at runtime when bcrypt>=4.1 is installed. Passlib’s internal version detection tries to access bcrypt.__about__.__version__, which was removed in newer bcrypt releases.
Symptoms:
AttributeError: module 'bcrypt' has no attribute '__about__'ValueError: password cannot be longer than 72 bytes(from passlib’s bug detection routine failing)- Server returns 500 on any endpoint that hashes or verifies passwords
Root Cause
Passlib (last release: 1.7.4, Jan 2020) relies on internal bcrypt APIs that changed in bcrypt 4.1+. Passlib is effectively unmaintained — no release in 6 years. The version detection code at passlib/handlers/bcrypt.py:620 accesses _bcrypt.__about__.__version__ which no longer exists.
Solution
Use bcrypt directly instead of through passlib. The bcrypt API is simple enough that the wrapper adds no value.
Dependency
dependencies = [ "bcrypt>=4.0.0", # NOT passlib[bcrypt]]Code
import bcrypt as _bcrypt
# Hash a password (registration)password_hash = _bcrypt.hashpw( password.encode(), _bcrypt.gensalt(rounds=12)).decode()
# Verify a password (login)is_valid = _bcrypt.checkpw( password.encode(), stored_hash.encode())Key details
.encode()/.decode()— bcrypt works with bytes, but store as string in DBrounds=12— production-grade cost factor (passlib defaults to 8)- Store the full hash string including algorithm prefix (
$2b$12$...)
Prevention
- Don’t depend on passlib for new projects — use
bcryptdirectly - Pin
bcrypt>=4.0.0to avoid pre-4.x API differences - If you must use passlib, pin
bcrypt<4.1alongside it