关于安全性:将旧密码移动到新的哈希算法?

Moving old passwords to new hashing algorithm?

我正在将一个站点切换到Rails。这是一个拥有5万多用户的大型网站。问题是,现有的密码散列方法非常薄弱。我有两个选择:

1)切换到新的算法,为每个人生成随机密码,然后将这些密码通过电子邮件发送给他们,并在之后立即要求更改。

2)执行新算法,但使用之前的旧算法,然后散列结果。例如:

密码:abcdef=algorithm 1=>xj31ndn=algorithm 2=>21aafadsada214美元

任何新的密码都需要通过原始算法(MD5),如果有任何意义的话,那么就需要对其结果进行散列。这有什么缺点吗?


通常不需要重置密码,只需等待用户下次登录即可。

  • 首先尝试用新算法验证输入的密码。新密码和已转换的密码将不会花费更长的时间进行验证。
  • 如果不匹配,请将其与旧的哈希算法进行比较。
  • 如果旧哈希值匹配,则可以计算并存储新哈希,因为您那时知道密码。
  • 每个密码存储系统都必须有切换到更好的哈希算法的选项,您的问题不是一次性迁移问题。好的密码散列算法(如bcrypt)有一个成本因素,有时您必须增加这个成本因素(因为硬件更快),然后您需要与迁移所需的完全相同的过程。

    如果您的第一个算法真的很弱,并且您希望立即提供更多的保护,那么对旧哈希进行哈希处理的选项2是一件好事。在这种情况下,您可以计算一个双哈希,并用新的双哈希替换数据库中的旧哈希。

    1
    $newHashToStoreInTheDb = new_hash($oldHashFromDb)

    您还应该标记此密码哈希(请参阅原因),以便将其识别为双哈希。这可以在单独的数据库字段中完成,也可以包括您自己的签名。现代密码散列函数还包括算法的签名,以便它们可以升级到新的算法,并且仍然可以验证旧的散列。示例显示bcrypt哈希的签名:

    1
    2
    3
    4
    $2y$10$nOUIs5kJ7naTuTFkBy1veuK0kSxUFXfuaOKdOKf9xYT0KKIGSJwFa
    ___
     |
     signature of hash-algorithm = 2y = BCrypt

    验证的运行方式如下:

  • 决定它是否是双哈希。
  • 如果是新的哈希,请调用新的哈希函数来验证输入的密码,这样就完成了。
  • 如果是双哈希,则将其与双哈希算法new_hash(old_hash($password))进行比较。
  • 如果双哈希值匹配,则可以计算并存储新哈希。

  • 最简单的解决方案可能是向数据库中添加"密码哈希类型"列。最初将其设置为"old";当用户登录时,使用新算法重新散列密码,并将数据库类型设置为"new"。

    此方法的一个变体是将哈希类型存储为哈希字符串的一部分。只要您能够清楚地区分不同的哈希格式,并且有一个优点,那就是您也可以在同一字符串中包含任何其他需要的参数(如salt和键拉伸的工作因子),而不必为每个参数向数据库中添加额外的字段。

    例如,这是现代unix crypt(3)实现(以及各种高级语言(如php)中的相应函数)通常使用的方法:基于des的(非常弱)密码散列看上去类似于abJnggxhB/yWI,而(稍微)更现代的散列可能类似于$1$z75qouSC$nNVPAk1FTd0yVd62S3sjR1,其中1sp指定了散列方法,z75qouSC是salt,nNVPAk1FTd0yVd62S3sjR1是实际散列,选择分隔符$是因为它不能出现在旧样式的des散列中。

    您建议的方法,其中新哈希计算为:

    1
     hash = new_hash( old_hash( password ) )

    在某些情况下很有用,因为它允许更新所有现有记录,而不必等待用户登录。但是,只有旧的hash函数保留了足够的密码熵才是安全的。

    例如,即使是一个相当老的弱密码散列函数(如未加保护的MD5)也足够好,因为它的输出依赖于整个输入,并且具有高达128位的熵,这几乎超过了任何密码所具有的熵(而且足够承受暴力攻击)。另一方面,使用旧的基于DES的crypt(3)函数作为旧的哈希来应用此构造将是灾难性的,因为旧的crypt(3)将忽略每个密码的前8个字符以外的所有字符(以及这些字符中最重要的位)。


    您可以创建一个新的密码字段,其中包含所有使用新密码方法更新密码的用户,只需使用选项2更新所有人。

    结合使用旧密码方法登录时强制更新所有用户的密码,将自动将所有活动用户移动到新密码方法。


    另一种选择是在数据库的单独列中为迁移阶段保留这两个哈希值:

    • 如果在登录期间新哈希不存在,请检查旧哈希并保存新哈希并删除旧哈希。
    • 如果新哈希存在,请仅使用此项进行验证。

    因此,一段时间后,您将只剩下新的哈希值——至少对于那些至少登录一次的用户而言是这样。