使用password_verify()函数验证MD5密码

3
有没有一种方法可以将 MD5 密码转换为可以由 password_verify() 验证的内容?
我在Crypt Wikipedia页面上读到:“可打印的 MD5 密码散列值以 $1$ 开头。”
因此,我尝试了以下方法(但没有成功):
$password = "abcd1234";
$md5hash = "$1$".md5($password);
var_dump(password_verify($password, $md5hash));

是否有办法让password_verify()MD5密码一起使用?原因是我有一个旧系统,其中密码以MD5哈希形式存储。我想开始使用更安全的Password Hashing API。如果我能够将现有的密码哈希转换为适用于password_verify()的格式,我可以更新数据库条目(在所有密码哈希前加上$1$),并使用以下代码使程序运行良好(而不必为旧的MD5密码制作特殊情况):
$password; // Provided by user when trying to log in
$hash; // Loaded from database based on username provided by user
if(password_verify($password, $hash)) {
   // The following lines will both update the MD5 passwords
   // and all passwords whenever the default hashing algorithm is updated
   if(password_needs_rehash($hash, PASSWORD_DEFAULT)) {
      $hash = password_hash($password, PASSWORD_DEFAULT);
      // Store the new hash in database
   }
   // User is logged in
} else {
   // User is not logged in
}

MD5哈希算法据说是单向的 - 这就是哈希的目的。然而,MD5已经被攻破,有一些网站拥有大量破解后的MD5哈希字典 - 你可以通过在它们的数据库中查找来转换密码?请看一下http://md5.wisetock.com/11347 - Professor Abronsius
最好的方法是让用户更改他们的密码。 - frz3993
不确定您在第一条评论中的意思,@frz3993。$1$确实是$md5hash的一部分,那么什么情况下$password不需要进行哈希处理呢?提示用户更改密码对我来说不是一个好的选择。如果我找不到问题的解决方案,我将制定一个特殊情况,在该情况下,当$hash不以$开头时,将$hashmd5($password)进行比较,而不是使用password_verify() - Magnar Myrtveit
@RamRaider 我认为将md5哈希转换为密码进行重新哈希不是一个好的解决方案,因为不同的密码可能会导致相同的md5哈希。如果我重新哈希的是与用户使用的密码不同的密码,则即使两个密码产生相同的md5哈希,重新哈希也无法与用户的密码配合使用。 - Magnar Myrtveit
1
@RamRaider,password_verify()函数将裸密码与哈希值进行比较,而不是比较哈希值。如果哈希值不以$开头,为什么不使用md5($password)进行比较?如果成功,使用password_hash函数对$password进行哈希并在数据库中更新它。 - frz3993
显示剩余2条评论
2个回答

6
你不能这样做。
你可以使用password_hash()对已经进行MD5哈希的密码进行哈希处理,并在数据库中加入一个额外的标记来表示这些旧密码,所以之后您需要进行双重验证。
以下是一些示例代码:
$passwordCompare = ($passwordIsOldFlag === true)
    ? md5($passwordInput)
    : $passwordInput;
if (password_verify($passwordCompare, $passwordHash)) { if ($passwordisOldFlag === true) { $passwordNewHash = password_hash($passwordInput, PASSWORD_DEFAULT);
// 在此处,您需要使用新的bcrypt哈希值更新数据库 // 并将passwordIsOldFlag设置为0 } }
注意:MD5生成32个字符长度的字符串,而password_hash()至少有60个字符长度。
阅读手册: 如果您决定使用password_hash()或兼容性包(如果PHP<5.5)https://github.com/ircmaxell/password_compat/,请注意,如果您当前的密码列的长度低于60,则需要将其更改为该长度(或更高)。手册建议长度为255。
您需要ALTER您的列长度并重新开始哈希才能生效。否则,MySQL将无声地失败。

再说了,把它写成这样可能是个好主意:“如果你决定使用password_hash()或兼容包(如果PHP < 5.5),可以参考https://github.com/ircmaxell/password_compat/”;-) - Funk Forty Niner
我不介意,尽管从技术上讲,我们只是在说要使用哪个函数,而不是提供它的包。 ;) 但更重要的是,即使是 PHP 5.4 现在也已经到了 EOL 的阶段,所以... - Narf
由于某些人可能被迫使用较低版本的PHP(我在一台服务器上工作时受到了限制,为什么呢?我不知道,我对此无法控制),因此他们可能无法使用password_hash()函数。所以我必须使用兼容包,并且其他人也可能被迫使用它。无论如何,这两种情况下,(password)列的长度都必须至少为60。 - Funk Forty Niner
1
是的,我知道那种痛苦...我认为将兼容包链接为注释就足够了,但请随意更新答案。 :) - Narf
完成。我添加了这个,以防有些人不愿意读评论;-) 干杯 - Funk Forty Niner

0

md5(或任何其他哈希算法-请参见使用hash_algos()的完整列表)将每次都将某些内容哈希为一个常量(相同的)内容,换句话说:

'1234' ---md5---> '81dc9bdb52d04dc20036dbd8313ed055'
'1234' ---md5---> '81dc9bdb52d04dc20036dbd8313ed055'
'1234' ---md5---> '81dc9bdb52d04dc20036dbd8313ed055'
'1234' ---md5---> '81dc9bdb52d04dc20036dbd8313ed055'
...

但是password_hash($password, $algo)每次都会将某些内容哈希成新的东西,换句话说:

'1234' ---password_hash---> '$2y$10$VXj5/N79aVolZQHJa.wdUub4C1uifXCNGRVKVUIYnsuRGs/wnXU/S'
'1234' ---password_hash---> '$2y$10$BjSPyCyZU2Rui5MtL5MLC.bkLGbUxf/f9NshALvTc39lhemoWZFC6'
'1234' ---password_hash---> '$2y$10$WXGX/6dCLbJN77MKNNVbCej9Fya2fQGvPjAMLuU3a6zGCDuBMisbm'
'1234' ---password_hash---> '$2y$10$sqkB2ZK7BanIHTRZIKUHi.TVdseZE.GSMghBhuT7mDC9GrjW9g6Ky'

因此,仅针对由password_hash()生成的哈希值,需要一个hash_verify_kind_function()函数,因为您可以使用md5(或任何其他算法)对字符串进行哈希处理,然后只需检查它们是否相等,这是非常愚蠢的工作,因为在这种情况下,您实际上不需要先使用md5进行哈希!因为实际上md5哈希不用于密码哈希(因此也不用于验证)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接