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 |
号
验证的运行方式如下:
最简单的解决方案可能是向数据库中添加"密码哈希类型"列。最初将其设置为"old";当用户登录时,使用新算法重新散列密码,并将数据库类型设置为"new"。
此方法的一个变体是将哈希类型存储为哈希字符串的一部分。只要您能够清楚地区分不同的哈希格式,并且有一个优点,那就是您也可以在同一字符串中包含任何其他需要的参数(如salt和键拉伸的工作因子),而不必为每个参数向数据库中添加额外的字段。
例如,这是现代unix crypt(3)实现(以及各种高级语言(如php)中的相应函数)通常使用的方法:基于des的(非常弱)密码散列看上去类似于
您建议的方法,其中新哈希计算为:
1 | hash = new_hash( old_hash( password ) ) |
在某些情况下很有用,因为它允许更新所有现有记录,而不必等待用户登录。但是,只有旧的hash函数保留了足够的密码熵才是安全的。
例如,即使是一个相当老的弱密码散列函数(如未加保护的MD5)也足够好,因为它的输出依赖于整个输入,并且具有高达128位的熵,这几乎超过了任何密码所具有的熵(而且足够承受暴力攻击)。另一方面,使用旧的基于DES的crypt(3)函数作为旧的哈希来应用此构造将是灾难性的,因为旧的crypt(3)将忽略每个密码的前8个字符以外的所有字符(以及这些字符中最重要的位)。
您可以创建一个新的密码字段,其中包含所有使用新密码方法更新密码的用户,只需使用选项2更新所有人。
结合使用旧密码方法登录时强制更新所有用户的密码,将自动将所有活动用户移动到新密码方法。
另一种选择是在数据库的单独列中为迁移阶段保留这两个哈希值:
- 如果在登录期间新哈希不存在,请检查旧哈希并保存新哈希并删除旧哈希。
- 如果新哈希存在,请仅使用此项进行验证。
因此,一段时间后,您将只剩下新的哈希值——至少对于那些至少登录一次的用户而言是这样。