6

我正在阅读 Cake 3 网站上的 Auth 文档但我仍然对如何将用户的密码迁移到我的新应用程序感到有些困惑。

它说要包含 FallbackPasswordHasher 类,但如果是这种情况,我应该在哪里放置旧密码的盐(因为它与新站点不同)?除此之外,一切似乎都是不言自明的。

我有几个不同但相关的网站,我将它们整合到一个网站中,该网站将为多个企业提供相同的服务,因此我需要从具有不同盐的各种网站导入用户密码。

4

2 回答 2

7

这取决于使用的旧哈希机制

根据所使用的机制和算法,您可能不需要旧盐,因为它已经附加到散列上,例如使用Blowfish密码散列器散列的密码或通常使用散列的密码的情况crypt(),它们可以被验证通过Default密码哈希。

因此,如果您的所有密码都是这种情况,那么您根本不需要使用Fallback密码散列器,而是可以只使用Default密码散列器,并在必要时重新散列密码,如文档中所示


没有加盐的哈希

对于使用不将盐附加到散列的机制进行散列的密码,例如md5()sha1()mhash()等,您可能需要一个自定义的旧密码散列器,该散列器包含已用于旧密码的盐。

在大多数情况下,WeakPassword哈希器应该完成这项工作,它使用旧的 Cake 2 机制和盐配置,即全局Security.salt选项和hashType哈希器选项。

请参阅Cookbook > 身份验证 > 更改散列算法

如果弱哈希器对您不起作用,例如您在旧应用程序中使用了自定义哈希器,那么您将需要自定义旧哈希器。可以在文档中找到遗留密码散列器的示例,合并盐应该像示例一样简单

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,请注意这里的所有示例代码都未经测试!

于 2014-12-07T00:35:44.570 回答
2

对于任何好奇这是如何解决的:这是我所做的,基于 ndm 的回答。

  1. 在 src 中创建一个名为 'Auth' 的文件夹(与控制器、模型等相同的目录在其中)
  2. 在该文件夹中,创建一个名为“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 和互联网。

于 2014-12-15T22:01:24.790 回答