为什么使用相同的盐和密码调用 encodePassword()(或 hashPasswor())会在 Symfony 中产生不同的哈希值?

Why does calling encodePassword() (or hashPasswor()) with identical salts and passwords produces diffent hashes in Symfony?

UserPasswordencoder,

public function encodePassword(UserInterface $user, string $plainPassword)
{
    $encoder = $this->encoderFactory->getEncoder($user);
    return $encoder->encodePassword($plainPassword, $user->getSalt());
}

编码器从用户实体获取盐。

我在用户实体中为 getSalt() 设置了一个静态变量:

public function getSalt()
{
    return 'my-static-salt';
}

但是当我编码时:

$password  = $encoder->encodePassword($user, "my-password");
$password2 = $encoder->encodePassword($user, "my-password");

$password$password2 彼此不同,就好像 encodePassword() 方法使用了随机盐。

我错过了什么?

Symfony > 5.4 的注意事项

从 Symfony 6 开始,这些 类 和方法的命名更恰当,用 Hash 替换了 Encode。并从 Security Core 包移至 Password Hasher 包:

例如, Symfony\Component\Security\Core\Encoder\EncoderFactory 变成 Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactory,依此类推。

但答案的实质是一样的。


默认情况下,EncoderFactory 会为您提供 NativePasswordEncoder 的实例(除非您安装了 libsodium 库,在这种情况下,它会为您提供 SodiumPasswordEncoder) .

如果你查看 NativePasswordEncoder::encodePassword() 你会看到 this:

public function encodePassword($raw, $salt)
{
    if (\strlen($raw) > self::MAX_PASSWORD_LENGTH) {
        throw new BadCredentialsException('Invalid password.');
    }

    // Ignore $salt, the auto-generated one is always the best

    $encoded = password_hash($raw, $this->algo, $this->options);

    if (72 < \strlen($raw) && 0 === strpos($encoded, '')) {
        // BCrypt encodes only the first 72 chars
        throw new BadCredentialsException('Invalid password.');
    }

    return $encoded;
}

注意这条评论:

// Ignore $salt, the auto-generated one is always the best

如果您不将盐字符串传递给 password_hash(),它会在您每次调用它时生成自己随机生成的盐,并将盐存储在操作结果中(以及使用的哈希算法).

(同样,在 SodiumPasswordEncoder 中你会看到 $salt is not used at all,尽管不存在类似的评论)。

进一步阅读: