How do you import members from another system to WordPress and update passwords so they’ll work?

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:

Read More
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 !!!

///////////////////////

Trying to override the ‘wp_check_password’ function that is pluggable in WP, but for some reason it doesn’t seem to be working.

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' );

Anybody have any ideas?

Related posts

4 comments

  1. I didn’t test this but in inc/wp-phpass.php in wordpress directory

    function crypt_private($password, $setting)
    {
        $output = '*0';
        if (substr($setting, 0, 2) == $output)
            $output = '*1';
    
        $id = substr($setting, 0, 3);
        # We use "$P$", phpBB3 uses "$H$" for the same thing
        if ($id != '$P$' && $id != '$H$')
            return $output;
    
        $count_log2 = strpos($this->itoa64, $setting[3]);
        if ($count_log2 < 7 || $count_log2 > 30)
            return $output;
    
        $count = 1 << $count_log2;
    
        $salt = substr($setting, 4, 8);
        if (strlen($salt) != 8)
            return $output;
    
        # We're kind of forced to use MD5 here since it's the only
        # cryptographic primitive available in all versions of PHP
        # currently in use.  To implement our own low-level crypto
        # in PHP would result in much worse performance and
        # consequently in lower iteration counts and hashes that are
        # quicker to crack (by non-PHP code).
        if (PHP_VERSION >= '5') {
            $hash = md5($salt . $password, TRUE);
            do {
                $hash = md5($hash . $password, TRUE);
            } while (--$count);
        } else {
            $hash = pack('H*', md5($salt . $password));
            do {
                $hash = pack('H*', md5($hash . $password));
            } while (--$count);
        }
    
        $output = substr($setting, 0, 12);
        $output .= $this->encode64($hash, 16);
    
        return $output;
    }
    

    as you can see they do use a simple md5 for password , you can change that to your own logic , BEWARE !! you have to manually change this every-time WordPress updates this file :/
    Not the best solution around but i hope this helps you

  2. This is not tested, so I can’t be positive it will work, but assuming USER_SALT is a constant like you have shown above, you should be able to drop this in as a plugin on your site and users should be able to validate using either the old hash or the default wordpress hash. Don’t forget to adjust the OLD_SALT.

    <?
    /*
    Plugin Name: Dual Password Checker
    Plugin URI: http://tapy.com
    Description: Checks passwords both with the wordpress hash and your old has
    Version: 0.0.1
    Author: tapy.com
    Author URI: http://tapy.com
    License: keep copyright in tact please
    */
    require_once(ABSPATH . 'wp-includes/class-phpass.php');
    class PasswordHashCustom extends PasswordHash{
        function CheckPassword($password, $stored_hash){
            return parent::CheckPassword($password, $stored_hash) ||
            md5(md5($password) . '56789') === $stored_hash;
        }
    }
    $wp_hasher = new PasswordHashCustom(8, TRUE);
    ?>
    
  3. The easiest way to accomplish this is to create a plugin. You can’t completely override a WP function without creating a plugin with the functionality. The functions you can completely override are in the ‘pluggable.php’ file. Which should be self explanatory that you’ll need to create a ‘plugin’ to override them.

    All I had to do was create a simple plugin:

    1. Add a directory to plugins: /plugins/my-password-updater
    2. Add the plugin file:

      /*
      Plugin Name: My Password Updater
      Plugin URI: Your Website
      Description: Checks both new and old passwords and updates old passwords
      Version: 0.0.1
      Author: Your Name
      Author URI: 
      License: 
      */
      
      if( ! function_exists( 'wp_check_password' ) ){
          function wp_check_password($password, $hash, $user_id = '') {
              global $wp_hasher;
      
              // Put whatever code from your old password hash check here
              // In my case it was the following...
              if ( $hash == md5( md5( $password ) . 'MY 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 );
              }
      
              // The code below is from the original function
              elseif ( strlen($hash) <= 32 ) {
                  $check = hash_equals( $hash, md5( $password ) );
                  if ( $check && $user_id ) {
                      wp_set_password($password, $user_id);
                      $hash = wp_hash_password($password);
                  }
                  return apply_filters( 'check_password', $check, $password, $hash, $user_id );
              }
      
              if ( empty($wp_hasher) ) {
                  require_once( ABSPATH . WPINC . '/class-phpass.php');
                  $wp_hasher = new PasswordHash(8, true);
              }
              $check = $wp_hasher->CheckPassword($password, $hash);
              return apply_filters( 'check_password', $check, $password, $hash, $user_id );
          }
      }
      
  4. Using the check_password filter should work. Here’s one I wrote for importing PBKDF2 SHA256 hashes, in the format that Django uses:

    add_filter( 'check_password', function( $check, $password, $hash, $user_id ) {
    
        /* Just to be safe, since I'm not a security expert, I don't want the
         * Django hash fallback to work for administrators. They will need to
         * reset their password the normal way. */
        $user = get_userdata( $user_id );
        if ( empty( $user ) || in_array( 'administrator', $user->roles ) ) {
            return $check;
        }
    
        /*
         * Example Django hash for password "school bus":
         *
         * pbkdf2_sha256$100000$BbirbJq1C1G7$IcYmssO2bsILHcTCzLxPs/YmVGNmKb3cSt2JWzVzP2I=
         *
         */
    
        $rv = preg_match( '/^pbkdf2_sha256$([0-9]+)$(.+)$(.+)$/', $hash, $matches );
        if ($rv === 1) {
            $iterations = intval( $matches[1] );
            $salt = $matches[2];
            $expected_base64_hash = $matches[3];
    
            $got_base64_hash = base64_encode( hash_pbkdf2( "sha256", $password, $salt, $iterations, 0, true ) );
    
            if ( $expected_base64_hash === $got_base64_hash ) {
                /* Save the password in WordPress default format, so that we
                 * can turn this plugin off some day. */
                wp_set_password( $password, $user_id );
    
                return true;
            } else {
                return false;
            }
        } elseif ( $rv === 0 ) {
            return $check;
        } else {
            error_log( "An error occurred with the regex in " . __FUNCTION__ . " " . __FILE__ );
            return $check;
        }
    }, 10, 4 );
    

Comments are closed.