密码哈希 - 如何升级?

时间:2010-10-17 20:48:53

标签: security database-design authentication hash passwords

最好的算法上有很多discussion - 但如果你已经投入生产了呢?如何升级而无需重置用户?


编辑/免责声明:虽然我最初想要一个“快速修复”解决方案并选择了orip的响应,但我必须承认,如果您的应用程序中的安全性非常重要,甚至可以解决这个问题,那么快速修复是错误的心态他提出的解决方案可能不够充分。

6 个答案:

答案 0 :(得分:24)

一个选项是使存储的哈希包含算法版本号 - 所以你从算法0开始(例如MD5)并存储

0:ab0123fe

然后当您升级到SHA-1时,将版本号颠倒到1:

1:babababa192df1312

(不,我知道这些长度可能不对)。

这样,您可以在验证密码时始终确定要检查的版本。您可以通过擦除以该版本号开头的存储哈希值来使旧算法无效。

如果您已经在生产中没有版本号的哈希,只需选择一个方案,以便您可以轻松识别未版本化的哈希 - 例如,使用上面的冒号方案,任何哈希根据定义,不包含冒号必须早于版本控制方案,因此可以推断为版本0(或其他)。

答案 1 :(得分:20)

保护所有现有密码的一种很酷的方法:使用现有的哈希作为新密码哈希的输入。

因此,如果您现有的哈希值是直接的MD5,并且您计划转移到某种形式的PBKDF2(或bcryptscrypt),那么请将密码哈希值更改为:< / p>

PBKDF2( MD5( password ) )

您已在数据库中安装了MD5,所以您只需将PBKDF2应用于它。

这种方法运行良好的原因是MD5与其他哈希值(例如SHA- *)的弱点不会影响密码使用。例如,它的碰撞漏洞对于数字签名是毁灭性的,但它们don't affect password hashes。与较长的哈希相比,MD5通过其128位输出稍微减少了哈希搜索空间,但与密码搜索空间相比,这是微不足道的,而密码搜索空间本身要小得多。

使密码散列强烈的原因是减慢速度(通过迭代在PBKDF2中实现)和随机的,足够长的盐 - 初始MD5不会对它们中的任何一个产生负面影响。

当你在这里时,也要在密码中添加一个版本字段。

编辑:加密StackExchange在此方法上有interesting discussion

答案 2 :(得分:9)

等到你的用户登录(所以你有明文密码),然后使用新算法和&将其保存在您的数据库中。

答案 3 :(得分:2)

一种方法是:

  • 为新密码引入新字段
  • 当用户登录时,检查旧哈希的密码
  • 如果正常,请使用新哈希
  • 对明文密码进行哈希处理
  • 删除旧哈希

然后逐渐只有新散列的密码

答案 4 :(得分:0)

您可能无法立即更改密码哈希方案,除非您以纯文本格式存储密码。您可以做的是在每个用户成功登录后使用更好的散列方案重新散列成员密码。

你可以试试这个:

首先在您的成员表中添加一个新列,或者在表中存储密码。

ALTER TABLE members ADD is_pass_upgraded tinyint(1) default 0;

接下来,在验证用户身份的代码中,添加一些额外的逻辑(我正在使用PHP):

<?php
$username = $_POST['username'];
$password = $_POST['password'];

$auth_success = authenticateUser($username, $password); 
if (!$auth_success) {
    /**
     * They entered the wrong username/password. Redirect them back
     * to  the login page.
     */
} else {
    /**
     * Check to see if the member's password has been upgraded yet
     */
    $username = mysql_real_escape_string($username);
    $sql = "SELECT id FROM members WHERE username = '$username' AND is_pass_upgraded = 0 LIMIT 1";
    $results = mysql_query($sql);

    /**
     * Getting any results from the query means their password hasn't been
     * upgraded yet. We will upgrade it now.
     */
    if (mysql_num_rows($results) > 0) {
        /**
         * Generate a new password hash using your new algorithm. That's
         * what the generateNewPasswordHash() function does.
         */
        $password = generateNewPasswordHash($password);
        $password = mysql_real_escape_string($password);

        /**
         * Now that we have a new password hash, we'll update the member table
         * with the new password hash, and change the is_pass_upgraded flag.
         */
        $sql = "UPDATE members SET password = '$password', is_pass_upgraded = 1 WHERE username = '$username' LIMIT 1";
        mysql_query($sql);
    }
}

您的authenticateUser()函数需要更改为类似的内容:

<?php
function authenticateUser($username, $password)
{
    $username = mysql_real_escape_string($username);

    /**
     * We need password hashes using your old system (md5 for example)
     * and your new system.
     */
    $old_password_hashed = md5($password);
    $new_password_hashed = generateBetterPasswordHash($password);
    $old_password_hashed = mysql_real_escape_string($old_password_hashed);
    $new_password_hashed = mysql_real_escape_string($new_password_hashed);

    $sql = "SELECT *
        FROM members
        WHERE username = '$username'
        AND
        (
            (is_pass_upgraded = 0 AND password = '$old_password_hashed')
            OR
            (is_pass_upgraded = 1 AND password = '$new_password_hashed')
        )
        LIMIT 1";
    $results = mysql_query($sql);
    if (mysql_num_rows($results) > 0) {
        $row = mysql_fetch_assoc($results);
        startUserSession($row);
        return true;
    } else {
        return false;
    }
}

这种方法有好处和缺点。从好处来看,个人会员的密码在登录后变得更加安全。缺点是每个人的密码都不安全。

我只会这样做2周。我会向所有会员发送电子邮件,告诉他们由于网站升级,他们有2周的时间登录他们的帐户。如果他们未能在2周内登录,则需要使用密码恢复系统重置密码。

答案 5 :(得分:0)

在下次验证纯文本时,只需重新哈希。哦,使用SHA-256和base256(全字节)的盐,大小为256字节。