Flask Authentication with WordPress 6.8 Password Hash Validation

I need help with user login verification in my Flask app that connects to a WordPress 6.8 database. The issue is with the new password hashing format that WordPress uses now.

After the WordPress upgrade to 6.8, the password hash format changed:

  • old format: $P$
  • new format: $wp$2y$10$

My original authentication worked perfectly before the update:

from passlib.hash import phpass

class UserValidator:
    def __init__(self, app):
        self.app = app
    
    def verify_user(self, username, plain_password):
        stored_hash = self.fetch_password_hash(username)
        # stored_hash from database: $P$.....
        # plain_password is user input: "mypassword"
        if not phpass.verify(plain_password, stored_hash):
            return False, Response.invalid_login(self.app.base_url)
        return True, None

I attempted to use flask_bcrypt for the new hash format but got Invalid salt error:

from flask_bcrypt import Bcrypt

class UserValidator:
    def __init__(self, app):
        self.app = app
        self.bcrypt = Bcrypt(app)
    
    def verify_user(self, username, plain_password):
        stored_hash = self.fetch_password_hash(username)
        # stored_hash looks like: $WP$2y$10$...
        # plain_password is user input: "mypassword"
        try:
            if not self.bcrypt.check_password_hash(stored_hash, plain_password):
                return False, Response.invalid_login(self.app.base_url)
        except Exception as error:
            print(error)  # shows: Invalid Salt
        return True, None

WordPress 6.8’s hash validation breaks because they rolled their own implementation instead of sticking to standard libraries. Hit this exact issue migrating a client’s Flask app last month. You need a hybrid validator that detects hash types on the fly and handles both formats. Don’t bother stripping prefixes or adding separate libraries - just build detection that reads the hash format and picks the right verification method. WordPress hashes have weird salt handling that’s different from standard bcrypt. Here’s the thing though - WordPress keeps backward compatibility under the hood, so you can actually tap into their validation logic. I’d suggest a middleware layer that normalizes auth regardless of hash format. Prevents auth failures during migrations and handles the messy cases where password resets mix formats. That error you’re getting? The salt extraction can’t parse WordPress’s custom format.

Been dealing with WordPress auth changes too and found something way better than parsing hash formats manually.

Skip the detection logic in Flask entirely. Set up an authentication service between Flask and WordPress that handles all the hash format mess without touching your main code.

It connects to your WordPress database, auto-detects old $P$ or new $2y$10$ formats, and just returns pass/fail to Flask. No more salt errors or format headaches in Python.

Best part? It scales when WordPress changes things again. Flask sends username/password, gets back auth results. The service deals with all WordPress weirdness.

Used this for multiple WordPress integrations - eliminates those random auth failures during transitions when users have mixed hash formats.

You can build this with Latenode: https://latenode.com

Had this exact issue when WordPress updated their hashing. WordPress 6.8 uses a modified bcrypt with the $wp$ prefix that standard bcrypt libraries can’t read. Strip the WordPress prefix before verifying. That $wp$2y$10$ format is just bcrypt with WordPress metadata. Remove the $wp$ part and use the remaining $2y$10$... with your bcrypt verification. What worked for me: check the hash format first, then route it properly. Old $P$ hashes go to phpass, new ones get the $wp$ stripped and go to standard bcrypt. It’s still bcrypt underneath - WordPress just slapped their own label on it. One heads up: during transitions your database might have both formats mixed together depending on when users last logged in. Build a detection system or you’ll get random auth failures.

Same boat here with WordPress integration nightmares. The 6.8 hash changes totally broke my auth flows.

Don’t fight the hash formats - automate the whole thing instead. Build a workflow that handles both old and new WordPress hashes automatically.

It’ll detect which hash format you’re using, route to the right verification method, and catch all the edge cases. Add fallback logic for mixed setups where some users still have old hashes.

I’ve done this for multiple WordPress versions and it beats handling everything in Flask code. The automation does format detection, verification, and password migration without breaking a sweat.

Bonus: you can extend it later for other WordPress auth methods or multiple instances without touching your Flask app.

Latenode works great for this kind of auth automation: https://latenode.com

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.

:thinking: 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.

:gear: 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.

:mag: 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.

:speech_balloon: 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!

This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.