如何在Python的Passlib上下文固定密码哈希值长度?
Great question! The long hash you're seeing is because Passlib's pbkdf2_sha256 scheme includes critical metadata (like the number of rounds, salt, and algorithm identifier) directly in the final hash string. That metadata is essential for verifying passwords later, but it does make the output longer than you want. Here's a secure way to get a fixed-length (30 or 40 character) output while keeping all the security benefits of slow password hashing:
Why Your Current Hash Is Long
Let's break down the hash you generated:
$pbkdf2-sha256$30000$X6vVGgNgzFmrlVIq5RyjVA$VGQ5x.yuabpdNMDMNc1S3/umqXMl3605DyjJ/lgXAM0
This string includes:
$pbkdf2-sha256$: Algorithm identifier30000: Number of PBKDF2 roundsX6vVGgNgzFmrlVIq5RyjVA: Random saltVGQ5x.yuabpdNMDMNc1S3/umqXMl3605DyjJ/lgXAM0: The actual derived key
All this metadata adds length, but we can't skip it—we need it to verify passwords. Instead, we'll take this full, secure hash and condense it to your desired fixed length.
Solution: Condense the Full Hash to Fixed Length
We'll generate the full PBKDF2 hash first (to keep security intact), then run it through a cryptographic hash function like SHA-256 to get a fixed-size digest, then encode and truncate it to your target length. Here's the modified code:
from passlib.context import CryptContext import hashlib import base64 # Initialize the Passlib context as before pwd_context = CryptContext( schemes=["pbkdf2_sha256"], default="pbkdf2_sha256", pbkdf2_sha256__default_rounds=30000 ) def encrypt_password(password, target_length=30): # Generate the full, secure PBKDF2 hash with metadata full_secure_hash = pwd_context.encrypt(password) # Create a fixed-size digest using SHA-256 sha256_digest = hashlib.sha256(full_secure_hash.encode("utf-8")).digest() # Encode to base64 (more compact than hex) and truncate to target length fixed_length_hash = base64.b64encode(sha256_digest).decode("utf-8")[:target_length] return fixed_length_hash def check_encrypted_password(password, stored_fixed_hash, target_length=30): # Generate the full hash for the input password full_secure_hash = pwd_context.encrypt(password) # Recreate the fixed-length hash using the same process as encryption computed_digest = hashlib.sha256(full_secure_hash.encode("utf-8")).digest() computed_fixed_hash = base64.b64encode(computed_digest).decode("utf-8")[:target_length] # Compare the stored and computed fixed-length hashes return computed_fixed_hash == stored_fixed_hash
Key Notes
- Security First: We're still relying on PBKDF2's slow hashing (which protects against brute-force attacks) for the core password processing. The SHA-256 step only condenses the output—it doesn't weaken the security.
- Consistency: Make sure you use the same
target_lengthvalue for both encryption and verification. If you switch between 30 and 40 characters, verification will fail. - Encoding Choice: Base64 gives you more characters per byte than hex, so you'll get a longer alphanumeric string for the same digest size. If you prefer hex, replace
base64.b64encodewithbinascii.hexlify(note: hex will produce longer strings for the same digest length).
Example Usage
# Encrypt a password to 30 characters hashed = encrypt_password("password", target_length=30) print(hashed) # Outputs something like "VGQ5x.yuabpdNMDMNc1S3/umqXMl3605" # Verify the password print(check_encrypted_password("password", hashed, target_length=30)) # True print(check_encrypted_password("wrong_pass", hashed, target_length=30)) # False
内容的提问来源于stack exchange,提问作者Urvi Soni




