passwordEncoder 为非身份验证目的生成的哈希在验证时失败

passwordEncoder generated hash for non-authentication purpose fails on verification

为了让用户在我的系统上注册,我发送了一封带有生成的 link 的确认邮件。单击时系统应将活动 属性 设置为 true。

为了创建确认 link,我生成了一个 md5 散列 random_byte 值。该值是我发送给用户的 link 的一部分。该值使用 passwordEncoder 进行哈希处理并存储在我的数据库中。

当用户单击 link 时,系统会根据我的数据库中的哈希检查该值,并且应该 return 为真,以完全激活用户。由于某种原因验证失败。

我检查过,register() 中生成的 $identifier 值是发送给用户的值。他们匹配。所以没有错。

起初我只想用password_verify(),但也没成功。

我根本不知道为什么它不起作用。我最后的猜测是,有一些我不知道的 Symfony 黑魔法在进行。

public function register(EntityManagerInterface $entityManager, Request $request, UserPasswordEncoderInterface $passwordEncoder, MailerInterface $mailer)
{
    // Register-Form
    $form = $this->createForm(RegisterFormType::class);

    $form->handleRequest($request);
    if ($form->isSubmitted() && $form->isValid()) {

        /** @var User $user */
        $user = $form->getData();

        $user->setPassword($passwordEncoder->encodePassword(
            $user,
            $form['plainPassword']->getData()
        ));

        // create activation hash
        $identifier = md5(random_bytes(16));

        $user->setActiveHash(
            $passwordEncoder->encodePassword(
                $user,
                $identifier
            )
        );


        // send activation mail with activation hash
        $message = (new TemplatedEmail())
            ->subject('...')
            ->from('...')
            ->to($user->getEmail())
            ->textTemplate('emails/registration.txt.twig')
            ->context([
                'mail' => $user->getEmail(),
                'identifier' => $identifier,
            ])
        ;

        $mailer->send($message);

        // persist & flush
        $entityManager->persist($user);
        $entityManager->flush();

        $this->addFlash('success', '...');

        //return $this->redirectToRoute('login');
        return $this->render('home/index.html.twig');
    }

    return $this->render('security/register.html.twig', [
        'registerForm' => $form->createView()
    ]);

}


/**
 * @Route("/confirm/{email}/{identifier}", name="register.confirm")
 */
public function confirm($email, $identifier, EntityManagerInterface $entityManager, UserPasswordEncoderInterface $passwordEncoder)
{
    $user = $entityManager->getRepository(User::class)->findOneBy([
        'email' => $email,
        'active' => false
    ]);

    if (!$user) {
        // fail no user with email
        die('no such user');
    }

    if( !$passwordEncoder->isPasswordValid($user, $identifier) ){
        // Hash verification failed
        die('hash failed');
    }

    $user->setActive(true);

    $entityManager->persist($user);
    $entityManager->flush();

    $this->addFlash('success', '...');

    return $this->redirectToRoute('login');

}

编辑: 我尝试用 password_hash('a_string', PASSWORD_ARGON2ID) 手动散列字符串,它抛出一个异常 PASSWORD_ARGON2ID 是一个未定义的常量。 PASSWORD_ARGON2I 有效。 Symfony 以某种方式使用 Argon2ID 作为我的密码字符串,而通过 encodePassword() 生成的 activeHash 字符串以“$argon2id...”开头,这怎么可能?

好的。所以我在本地通过 MAMP 是 运行 PHP 7.2。正如 msg 在评论中提到的(在删除之前),Argon2ID 哈希可能是由 Sodium 提供的。另一方面,7.2 password_verify() 无法验证 argon2id 哈希。

已升级到 PHP 7.3.7,没有问题了。