Our current system ( not using WordPress ) has 1000s of users that we need to port over to WordPress. The problem we’re running into is that the passwords can’t stay the same.

In our current system the passwords are saved with:

md5( md5( $password ) . USER_SALT ); // USER_SALT is a defined constant

Not the best obviously, but not the worst…

We need to make these password hashes that we currently have work in WP as well. Is there a way we can run all new passwords through this setup first and then through WPs own hashing?

I know you can hook into the functions like:

function my_hash_password($password){
    return md5( md5( $password ) . USER_SALT );
}
add_action('wp_hash_password', 'my_hash_password' );

For some reason that’s not fully working either.

Surely somebody else has already gone through this before.

Thanks.

EDIT !!!!

So far there is some confusion. I am NOT asking no un-hash the hashed password that we have. What I am saying is that with our current system the passwords look like:

Password:  password
Hash function: md5( md5( $password ) . USER_SALT );
Output: d372f9c033e9c358b111ff265e080d3a

I want to ‘maybe’ be able to take the hash above and feed it to the native WP password hasher so that:

d372f9c033e9c358b111ff265e080d3a

becomes…

$P$BdrwxndTzgTVHUozGpQ9TEMYd6mpTw0

after it goes through their function.

Then when a user logs in we send their plain text password back through our function and then through WPs to get a match.

UPDATE !!!

So following the advice from rarst below I’ve done this:

function my_check_password($password, $hash, $user_id = '') {
    global $wp_hasher;
    if ( $hash == md5( md5( $password ) . USER_SALT ) ){
        if ( $user_id ) {
        $check = true;
        wp_set_password($password, $user_id);
        $hash = wp_hash_password($password);
      }
      return apply_filters( 'check_password', $check, $password, $hash, $user_id );
    }

    // If the hash is still md5...
    elseif ( strlen($hash) <= 32 ) {
        $check = hash_equals( $hash, md5( $password ) );
        if ( $check && $user_id ) {
            // Rehash using new hash.
            wp_set_password($password, $user_id);
            $hash = wp_hash_password($password);
        }
        return apply_filters( 'check_password', $check, $password, $hash, $user_id );
    }

    // If the stored hash is longer than an MD5, presume the
    // new style phpass portable hash.
    if ( empty($wp_hasher) ) {
        require_once( ABSPATH . WPINC . '/class-phpass.php');
        // By default, use the portable hash from phpass
        $wp_hasher = new PasswordHash(8, true);
    }

    $check = $wp_hasher->CheckPassword($password, $hash);

    /** This filter is documented in wp-includes/pluggable.php */
    return apply_filters( 'check_password', $check, $password, $hash, $user_id );
}
add_action('wp_check_password', 'my_check_password' );

But this is still not working. What am I missing?

1 Answer
1

You couldn’t have a more perfect solution, since this is precisely what WordPress core itself has to handle.

See, in early years WP used just one pass MD5 for passwords. Then they went to better hashing, but obviously it should have kept working with old hashes.

Now if you take a look at wp_check_password(), what it is doing (at the moment user attempts to log in) is following:

  1. Checks if hash looks “old”
  2. If so verifies it in “old” mode
  3. If so updates hash to “new” one (using password, which is available at this moment in runtime)
  4. Otherwise verifies in “new” mode

You should simply follow precise same logic! Hook into check_password or override wp_check_password() altogether (it’s so called pluggable function). Then implement same check & upgrade logic for your hashes.

Leave a Reply

Your email address will not be published. Required fields are marked *