0

I migrated users from Yii2 to Drupal 9, where there are password hashes like $2y$13$nhBZ37OKp27jJBLgRkFlOuL5ac8Bda5n2XIZTjKON5gKPfIcxh61y genereated by Yii2 with \Yii::$app->security->generatePasswordHash("123qweASD")).

I migrated them 1:1.

Migrating users - advanced password examples says:

If the source system was a PHP based application that used crypt() or password_hash(), these hashes should work as-is and they can be migrated 1:1 like in the Phpass example above .When the user logs in to your Drupal 8 site for the first time, Drupal will re-hash the password.

The password are not correct.

I tried to manually insert various hashes into the database according to this algorithm (crypt() with costFactor 13) generated in Yii2, but this also leads to a login error.

I found the following code in Yii2 (User.php).

/**
 * Validates password
 *
 * @param string $password password to validate
 * @return boolean if password provided is valid for current user
 */
public function validatePassword($password)
{
    if(!$password) return false;
    return Yii::$app->security->validatePassword($password, $this->password);
}

In Security.php, it uses the following code.

/**
 * Verifies a password against a hash.
 * @param string $password The password to verify.
 * @param string $hash The hash to verify the password against.
 * @return boolean whether the password is correct.
 * @throws InvalidParamException on bad password/hash parameters or if crypt() with Blowfish hash is not available.
 * @throws InvalidConfigException when an unsupported password hash strategy is configured.
 * @see generatePasswordHash()
 */
public function validatePassword($password, $hash)
{
    if (!is_string($password) || $password === '') {
        throw new InvalidParamException('Password must be a string and cannot be empty.');
    }

    if (!preg_match('/^\$2[axy]\$(\d\d)\$[\.\/0-9A-Za-z]{22}/', $hash, $matches) || $matches[1] < 4 || $matches[1] > 30) {
        throw new InvalidParamException('Hash is invalid.');
    }

    switch ($this->passwordHashStrategy) {
        case 'password_hash':
            if (!function_exists('password_verify')) {
                throw new InvalidConfigException('Password hash key strategy "password_hash" requires PHP >= 5.5.0, either upgrade your environment or use another strategy.');
            }
            return password_verify($password, $hash);
        case 'crypt':
            $test = crypt($password, $hash);
            $n = strlen($test);
            if ($n !== 60) {
                return false;
            }
            return $this->compareString($test, $hash);
        default:
            throw new InvalidConfigException("Unknown password hash strategy '{$this->passwordHashStrategy}'");
    }
}

Yii 2.0.6 runs on PHP 5.6, while Drupal 9.4.5 runs on PHP 8.1.4.

How do I migrate password hashes?

2

1 Answer 1

3

That doc page seems to apply to Drupal 10, not Drupal 9. see: https://www.drupal.org/node/3322420

If you need to use Drupal 9 you could backport the code and/or write your own class the implements the same interface as

Drupal\Core\Password\PhpassHashedPassword

In Drupal 9 core.services.yml you wold need to swap your replacement class for that one in the core service definition.

service:
  password:
    class: Drupal\Core\Password\PhpassHashedPassword
    arguments: [16]

Altering is covered in the doc below, though there is a simpler way of using a replacement definition in the services file in sites/default/services.yml

https://www.drupal.org/docs/drupal-apis/services-and-dependency-injection/altering-existing-services-providing-dynamic-services

1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.