我正在阅读 Cake 3 网站上的 Auth 文档,但我仍然对如何将用户的密码迁移到我的新应用程序感到有些困惑。
它说要包含 FallbackPasswordHasher 类,但如果是这种情况,我应该在哪里放置旧密码的盐(因为它与新站点不同)?除此之外,一切似乎都是不言自明的。
我有几个不同但相关的网站,我将它们整合到一个网站中,该网站将为多个企业提供相同的服务,因此我需要从具有不同盐的各种网站导入用户密码。
根据所使用的机制和算法,您可能不需要旧盐,因为它已经附加到散列上,例如使用Blowfish
密码散列器散列的密码或通常使用散列的密码的情况crypt()
,它们可以被验证通过Default
密码哈希。
因此,如果您的所有密码都是这种情况,那么您根本不需要使用Fallback
密码散列器,而是可以只使用Default
密码散列器,并在必要时重新散列密码,如文档中所示。
对于使用不将盐附加到散列的机制进行散列的密码,例如md5()
、sha1()
、mhash()
等,您可能需要一个自定义的旧密码散列器,该散列器包含已用于旧密码的盐。
在大多数情况下,WeakPassword
哈希器应该完成这项工作,它使用旧的 Cake 2 机制和盐配置,即全局Security.salt
选项和hashType
哈希器选项。
如果弱哈希器对您不起作用,例如您在旧应用程序中使用了自定义哈希器,那么您将需要自定义旧哈希器。可以在文档中找到遗留密码散列器的示例,合并盐应该像示例一样简单
namespace App\Auth;
use Cake\Auth\AbstractPasswordHasher;
class LegacyPasswordHasher extends AbstractPasswordHasher {
public function hash($password) {
throw new \LogicException('You really should not use me!');
}
public function check($password, $hashedPassword) {
// compare using the legacy salt
return sha1($password . 'legacy-salt-here') === $hashedPassword;
}
}
请注意,在使用Fallback
散列器的情况下,不需要实现传统散列器hash()
来实际散列某些东西,因为您不应该将它用于新散列。
拥有来自使用单独盐的不同来源的密码会使事情变得更复杂一些。
有多种方法可以解决这个问题,最明显的一种(因为这是Fallback
哈希器的工作方式)是为每个应用程序创建一个单独的自定义遗留哈希器,其中不同的哈希器将使用适当的盐,也许还有特定应用程序的算法。
然后,您需要做的就是依靠Fallback
散列器迭代所有旧散列器,直到其中一个成功验证密码。
'passwordHasher' => [
'className' => 'Fallback',
'hashers' => ['Default', 'LegacyApp1', 'LegacyApp2', 'LegacyApp3', /* ... */]
]
基本上,是的,这里唯一的问题可能是冲突,即App X的哈希器将成功验证密码,该密码通常由密码来源的App Yfalse
的哈希器评估。
如果出于某种原因可能会出现这种情况(即使对于非常弱的 MD5,它通常也不太可能发生),您必须寻求更严格的解决方案。
可能有 10^Cake 方法来解决这样的问题,所以由于冲突应该不太可能发生,我会坚持我认为最基本的方法,即在合并密码之前修改密码,以便它们具有标识符添加了标识旧应用程序的旧应用程序,然后旧密码哈希器可以使用它来选择适当的盐和算法。
例如,如果App 1的旧密码是
de1566cc82d9fda1ac39a28a45afe3671d9ef880
前置app1
(使用在任何旧密码和新密码哈希中都不会出现的唯一分隔符)以使密码列包含类似
app1:::de1566cc82d9fda1ac39a28a45afe3671d9ef880
并在您的旧密码哈希器中检查它是否执行类似的操作
public function check($password, $hashedPassword) {
if(strpos($hashedPassword, ':::') === false) {
// this is not the password you are looking for
return false;
}
// separate the identifier from the password
list($app, $hashedPassword) = explode(':::', $hashedPassword, 2);
// create comparision hash using app specific legacy algorithms and salts
switch($app) {
case 'app1':
$compare = sha1($password . '375828236784563245364');
break;
case 'app2':
$compare = md5('legacy-salt-for-app-2' . $password);
break;,
// etc
default:
return false;
}
return $compare === $hashedPassword;
}
正如最初提到的,有很多方法可以解决这个问题,显示的只是其中一种,但是这应该提示您通常需要完成这样的任务。
ps,请注意这里的所有示例代码都未经测试!
对于任何好奇这是如何解决的:这是我所做的,基于 ndm 的回答。
在该文件夹中,创建一个名为“LegacyPasswordHasher.php”的文件。使用类似于以下代码的代码:
namespace App\Auth;
use Cake\Auth\AbstractPasswordHasher;
class LegacyPasswordHasher extends AbstractPasswordHasher {
public function hash($password) {
throw new \LogicException('You really should not use me!');
// shamelessly copied from ndm's result - a 'hash' function is necessary for this class
}
public function check($password, $hashedPassword) {
// compare using the legacy salt
return sha1('myAwesomeAndStrangeSaltFromPreviousSite'.$password) === $hashedPassword;
}
}
请记住,有些人在加盐之前,有些人在加盐。确保您知道它是哪个,否则您的旧版登录将不起作用。
之后,进入您的 src/AppController.php 文件并将其添加到您的公共 $components 数组下:
'authenticate' => [
'Form' => [
'passwordHasher' => [
'className' => 'Fallback',
'hashers' => ['Default', 'Legacy']
// 'Default' is your main one - I added 'Legacy' here
// 'Legacy is called if 'Default' fails - refers to our php file we just made
]
]
]
在此之后,我构建了一个函数,如果用户的密码过期,它会将用户重定向到密码重置页面。我将所有旧用户导入到我的网站,将他们所有的密码到期日期批量设置为几年前,然后通过使用旧网站的 salt 加密的密码登录来测试其中一个。它让我完美登录,检测到我过期的密码,将我重定向到我的“设置新密码”页面,在他们设置新密码后,它使用 CakePHP 3 的当前加密方法(我认为是 sha256)对其进行了加密。
完美的!再次感谢 StackOverflow、ndm 和互联网。