The Problem:
You’re experiencing authentication issues in your Flask application due to changes in WordPress 6.8’s password hashing mechanism. Your existing phpass-based authentication breaks because WordPress 6.8 now uses a modified bcrypt algorithm with a $wp$ prefix, causing flask_bcrypt to throw an Invalid salt error.
Understanding the “Why” (The Root Cause):
WordPress 6.8 introduced a new password hashing scheme for improved security. While it’s based on bcrypt, it adds a custom prefix ($wp$) to the hash. Standard bcrypt libraries, like flask_bcrypt, don’t inherently recognize or handle this prefix, leading to the Invalid salt error. The issue isn’t with the core bcrypt algorithm itself, but with the added WordPress-specific metadata in the hash. Your previous phpass method is also incompatible as it handles a different hashing algorithm entirely.
Step-by-Step Guide:
Step 1: Direct Bcrypt Implementation and Prefix Removal
Instead of relying on flask_bcrypt, directly utilize the bcrypt library and pre-process the password hash retrieved from the WordPress database. This involves removing the $wp$ prefix before verification.
import bcrypt
class UserValidator:
def __init__(self, app):
self.app = app
def verify_user(self, username, plain_password):
stored_hash = self.fetch_password_hash(username)
#Example stored_hash: $wp$2y$10$...
if stored_hash.startswith('$wp$'):
stored_hash = stored_hash[4:] # Remove the '$wp$' prefix
try:
#Convert plain_password to bytes for bcrypt comparison
plain_password_bytes = plain_password.encode('utf-8')
stored_hash_bytes = stored_hash.encode('utf-8')
if not bcrypt.checkpw(plain_password_bytes, stored_hash_bytes):
return False, Response.invalid_login(self.app.base_url)
return True, None
except Exception as e:
print(f"Bcrypt verification error: {e}")
return False, Response.invalid_login(self.app.base_url)
Step 2: Handle Legacy $P$ Hashes (Optional):
If your database contains a mix of old $P$ (phpass) and new $wp$ hashes, you’ll need to add logic to detect and handle both formats appropriately. This could involve checking the first three characters of the stored_hash to determine the algorithm and using the correct library for verification.
import bcrypt
from passlib.hash import phpass
class UserValidator:
# ... (previous code) ...
def verify_user(self, username, plain_password):
stored_hash = self.fetch_password_hash(username)
if stored_hash.startswith('$P$'):
if not phpass.verify(plain_password, stored_hash):
return False, Response.invalid_login(self.app.base_url)
return True, None
elif stored_hash.startswith('$wp$'):
# ... (bcrypt handling from Step 1) ...
Step 3: Verify Database Connection and Data Retrieval:
Ensure your fetch_password_hash function correctly connects to the WordPress database and retrieves the password hash without errors. Double-check the database credentials and the query used to fetch the password.
Common Pitfalls & What to Check Next:
- Encoding: Ensure consistent encoding (e.g., UTF-8) throughout your code, especially when comparing bytes objects in bcrypt.
- Mixed Hash Formats: If you’re migrating from an older WordPress version, expect a mix of old and new hashes. Implement robust handling for both (see Step 2).
- Database Errors: Check your database connection and the query used to retrieve password hashes. Any errors in this process will lead to authentication failures.
APPLICATION_PASSWORD_SCHEME Filter: This WordPress filter might interfere with external authentication; check if it’s enabled and how it impacts your authentication flow.
Still running into issues? Share your (sanitized) config files, the exact command you ran, and any other relevant details. The community is here to help!