【问题标题】:PHP Password Rehashing in User Class (MySQL)用户类中的 PHP 密码重散列(MySQL)
【发布时间】:2020-06-05 09:07:27
【问题描述】:

所以我正在处理合并重新散列以升级用户以拥有 bcrypt 密码的功能,并将其合并到我发现并已成功设置的现有课程中,这太棒了。

但是,此类缺少重新散列检查,这对于现有用户数据库上的旧密码来说是很糟糕的。我们需要处理 SHA1 密码!我们使用 SHA1 + Salt,所以我希望这可以转换。

我正在使用这里找到的这个类:

https://alexwebdevelop.com/user-authentication/

所以使用这个类,我添加了以下公共函数:

public function authenticate($username, $password)
{
    /* Global $pdo object */
    global $pdo;

    // Database lookup
    $stmt = $pdo->prepare("SELECT id, password, legacy_password FROM users WHERE username = ?");
    $stmt->execute([$username]);
    $stored = $stmt->fetch(PDO::FETCH_ASSOC);
    if (!$stored) {

        // No such user, throw an exception
        throw new Exception('Invalid user.');
    }
    if ($stored['legacy_password']) {

        // This is the legacy password upgrade code
        if (password_verify(sha1($password), $stored['password'])) {
            $newHash = password_hash($password, PASSWORD_DEFAULT);
            $stmt    = $pdo->prepare("UPDATE users SET password = ?, legacy_password = FALSE WHERE id = ?");
            $stmt->execute([$newhash, $stored['id']]);

            // Return the user ID (integer)
            return $stored['id'];
        }
    } elseif (password_verify($password, $stored['password'])) {

        // This is the general purpose upgrade code e.g. if a future version of PHP upgrades to Argon2
        if (password_needs_rehash($stored['password'], PASSWORD_DEFAULT)) {
            $newhash = password_hash($password, PASSWORD_BCRYPT);
            $stmt    = $pdo->prepare("UPDATE users SET password = ? WHERE id = ?");
            $stmt->execute([$newhash, $stored['id']]);
        }

        // Return the user ID (integer)
        return $stored['id'];
    }

    // When all else fails, throw an exception
    throw new Exception('Rehashing failed.');
}

现在在类的 login() 函数里面,我已经替换了

public function login($name, $passwd)
    {
...
    if (is_array($row)) {

        if (password_verify($passwd, $row['password'])) {
                        /* Authentication succeeded. Set the class properties (id and name) */
                        $this->id            = intval($row['id'], 10);
                        $this->name          = $name;
                        $this->authenticated = TRUE;

                        /* Register the current Sessions on the database */
                        $this->registerLoginSession();

                        /* Finally, Return TRUE */
                        return TRUE;
                    }
    }
}

有了这个:

public function login($name, $passwd)
    {
...
        if (is_array($row)) {

            $userid = $this->authenticate($name, $row['password']);

            if (password_verify($passwd, $row['password'])) {
                /* Authentication succeeded. Set the class properties (id and name) */
                $this->id            = intval($userid);
                $this->name          = $name;
                $this->authenticated = TRUE;

                /* Register the current Sessions on the database */
                $this->registerLoginSession();

                /* Finally, Return TRUE */
                return TRUE;
            }

        }
}

因此它应该在检查/重新散列后将手返回 ID。所以它发现我是一个用户,经过测试。好..所以现在所有 authenticate() 所做的都是抛出失败的异常错误。我不知道如何从中获取错误消息。

这似乎与此 ID 的确切关系,我做错了什么?

这一点:用户在表单中使用SHA1(salted)密码登录,脚本重新哈希密码,用户像什么都没发生一样登录。

authenticate()我正在使用的转换函数:

https://paragonie.com/blog/2016/02/how-safely-store-password-in-2016#legacy-hashes

【问题讨论】:

  • 有两件事看起来很奇怪:你提到你的 SHA1 哈希值是加盐的,但在你的新代码中没有提到盐;然后您在authenticate 方法之外第二次调用password_verify。但我不清楚你的实际问题是什么。您是说authenticate 函数正在访问您的throw Exception 行之一吗?哪一个?使用什么预期场景(旧密码或非旧密码)?
  • 我认为在password_verify(sha1($password), $stored['password'])中,您需要检查sha1($password) == $stored['password'],因为password_verify()会在比较之前再次处理密码。
  • legacy_password = FALSE 这将是 legacy_password = NULL 作为它的 varbinary 对吗?
  • 为什么在你的函数之上还有另一个密码验证已经完成了自己的密码验证。在遗留块上,您为什么还要在密码验证中提供它?我也不确定为什么您需要重新哈希每次登录
  • 在遗留块中只需简单地使用sha1($password) === $stored['password'],然后应用新的bcrypt,另一方面只需简单地password_verify

标签: php mysql sha1 crypt


【解决方案1】:

对不起!我从这里的建议中学到了东西,感谢所有的帮助!

所以我自己解决了这个问题。我所做的是删除 authenticate() 函数,而是直接根据反馈 cmets 解决这个问题(我完全同意)。

我将帖子中的最后一个代码块替换为:

if (is_array($row)) {

    if (password_needs_rehash($row['password'], PASSWORD_DEFAULT)) {

        $newhash = password_hash($passwd, PASSWORD_BCRYPT);
        $stmt    = $pdo->prepare("UPDATE users SET password = ? WHERE id = ?");
        $stmt->execute([$newhash, $row['id']]);

    }
    if (password_verify($passwd, $row['password'])) {

        /* Authentication succeeded. Set the class properties (id and name) */
        $this->id            = intval($row['id'], 10);
        $this->name          = $name;
        $this->authenticated = TRUE;

        /* Register the current Sessions on the database */
        $this->registerLoginSession();

        /* Finally, Return TRUE */
        return TRUE;
    }

}

并且用户密码正在重新散列,并登录!

【讨论】:

    猜你喜欢
    • 2018-08-19
    • 1970-01-01
    • 1970-01-01
    • 2015-08-13
    • 2012-05-30
    • 2019-02-15
    • 2014-09-09
    • 2012-07-06
    • 2013-06-07
    相关资源
    最近更新 更多