如何升级密码存储方案(更改哈希算法)

28

我被要求对一个内部网站进行一些更改/更新; 使其成为所谓的“未来证明”。

我们发现密码是使用MD5算法进行哈希的。(该系统自2001年以来存在,因此当时是足够的)。
我们现在想将哈希算法升级为更强的算法(BCrypt-hash或SHA-256)。

显然我们不知道明文密码,并且为用户群创建新密码不是一个选项*)

那么我的问题是:

没有访问明文密码的情况下更改哈希算法的接受方式是什么?
最好的解决方案是完全“在幕后”解决的方法。

*) 我们尝试过; 尝试说服他们,使用“密码年龄”的论据,用咖啡贿赂他们,用蛋糕贿赂他们,等等。但这不是选项

更新
我希望有某种自动化解决问题的解决方案,但显然除了“等待用户登录,然后转换”之外别无选择。

好吧,至少现在我知道没有其他可用的解决方案。

7个回答

34

首先,在数据库中添加一个字段来标识密码是否使用MD5或新算法。

对于仍在使用MD5的所有密码:

-- 在登录过程中,验证用户输入的密码时:将用户提交的密码暂时存储在内存中(这里没有安全问题,因为它已经在某个地方存储在内存中),然后进行通常的MD5哈希和与存储的哈希比较;

-- 如果给出了正确的密码(与现有哈希匹配),则通过新算法运行临时存储的密码,存储该值,并更新新字段以标识此密码已更新为新算法。

(当然,您只需为任何新用户/新密码使用新算法。)


7

我对这个选项并不完全确定,因为我不是密码学专家。如果我在这里有任何错误,请纠正我!

我认为Dave P.的选择显然是最好的。

...但是,有一个自动化的解决方案-将旧哈希本身进行哈希处理。也就是说,使用更强的算法再次对当前哈希进行哈希处理。请注意,据我所知,您在这里无法从哈希长度中获得任何额外的安全性,只能获得新算法的加密复杂性。

问题是,当然,检查密码将必须通过两个哈希。而且,您还必须为每个新密码执行相同的操作。这很愚蠢。除非您想使用类似Dave P.解释的方案,最终可以回到使用新哈希算法的单哈希密码...在这种情况下,为什么要费心呢?(毫无疑问,您可能会在向企业人士展示时以相对严肃的面孔使用它,以“立即应用所有密码的改进安全性!”的方式进行炫耀...)

尽管如此,这是一个可以立即应用于所有当前密码的选项,而无需进行任何逐步迁移阶段。

但是,哦,天哪,有人以后看到那些代码一定会大笑!:)


这听起来像是我正在寻找的东西。但是我不确定“哈希一个哈希”的影响是什么。我记得在某处读到过,在某些条件下结果可能不太优秀。但我不确定。 - Jacco
2
这是一个不好的想法,因为如果第一个哈希中有冲突,那么在组合算法中也会有冲突。而且MD5已知存在碰撞。 - Richard Gadsden
2
@Richard,在这种情况下,哈希碰撞会有什么影响吗? - Ilari Kajaste
1
我可以看到这个方案可行,它感觉很丑陋,但实际上并不是。首先MD5哈希几乎不花费任何成本(这就是为什么它不再有用),然后你使用更好的算法来哈希哈希值。这样可以处理整个数据库,接着在登录时也做同样的事情。或者更好的方法是,在不改变注册流程的情况下处理整个数据库,然后将登录流程改为尝试两次,第一次使用明文 -> 新算法,如果失败,则尝试2次md5(明文) ->新算法以覆盖已经哈希的哈希值。因此,旧用户的登录速度会稍微慢一些,因为它会尝试两次... 嗯。 - Chris Baker

3

在数据库中添加passwordChange日期时间字段。

所有在X日之前设置的密码,使用MD5进行检查。

所有在X日之后设置的密码,使用BCrypt或其他方法进行检查。


当用户从MD5登录时,将其密码更改为BCrypt。 - Dennis

3

您可以将哈希字段本身(例如“MD5:d41d8cd98f00b204e9800998ecf8427e”)或另一列中使用的算法存储起来,然后需要修改登录过程以在检查密码时使用正确的算法。自然地,任何新密码都将使用新算法进行哈希处理。希望密码最终会过期,并且随着时间的推移,所有MD5哈希都会逐渐淘汰。


1
如果您在登录时可以访问明文密码,那么您也可以将登录过程“升级”到新的哈希算法来加密密码。 - Craig

3

由于您不知道明文密码,也许您应该创建一个字段来指示加密版本(例如PasswordVersion bit default 0)。

下次用户尝试登录时,请使用当前算法版本检查哈希密码,就像今天一样。如果匹配,则再次进行哈希处理并更新PasswordVersion字段。

希望您不需要比bit更大的PasswordVersion列。 =)


希望你不需要比特位更大的PasswordVersion列,这让我微笑了(+1) - Jacco
真的,不要假设你这次做对了。请使用 byte 类型来表示你的密码版本。 - Richard Gadsden
1
只需要8次尝试就能做对吗?将其转换为“int”,你就有足够的空间来制定自己的错误集合了... =) - Rubens Farias

3
您应该更改您的密码数据库以存储三个项目:
  1. 算法标识符。
  2. 服务器在第一次计算和存储密码哈希时选择的随机盐字符串。
  3. 使用指定算法的salt+password串的哈希值。
当然,这些可以只存储在一个文本字段中,并用分隔符分隔:

"SHA256:这是盐:这是哈希值"

现在将您现有的条目转换为一个具有空盐和旧算法的值。

"MD5::这是没有盐的旧MD5哈希"

现在您有足够的信息来验证所有现有的密码条目,但您也可以验证新条目(因为您知道使用了哪个哈希函数)。下次现有用户登录时,您可以将旧条目转换为新算法,因为此过程中您将拥有其密码:
  1. 如果您的数据库表明他们正在使用旧算法而没有盐,请首先通过检查密码的MD5哈希是否匹配来验证密码。如果不是,则拒绝登录。
  2. 如果验证了密码,请让服务器选择一个随机盐字符串,计算盐+密码的SHA256哈希值,并用新算法,盐和哈希替换密码表项。
  3. 当用户再次登录时,您将看到他们正在使用新算法,因此计算盐+密码的哈希值并检查其是否与存储的哈希值匹配。
最终,在该系统运行了适当的时间之后,您可以禁用未转换的帐户(如果需要)。
唯一有区别的是每个条目添加了一个随机的盐字符串,这使得该方案更加抵抗使用彩虹表的字典攻击。

我不确定我理解你的评论,除非你所说的“使用适当的哈希”是指在使用MD5哈希密码之前已经添加了盐值。如果是这样,升级方案仍然有效,您只需要在使用MD5进行验证时考虑盐。 - Stephen C. Steel

0
最佳答案来自真正的密码学专家 https://paragonie.com/blog/2016/02/how-safely-store-password-in-2016#legacy-hashes 该帖子还有助于解释应使用哪种哈希函数。即使它说的是2016年,但仍然适用于当前。如果不确定,请使用bcrypt。
在用户帐户表中添加一个名为legacy_password(或等效)的列。这只是一个布尔值。
计算现有密码哈希的新加强哈希,并将它们存储在数据库中。
修改您的身份验证代码以处理遗留标志。
当用户尝试登录时,首先检查是否设置了legacy_password标志。如果是,则首先使用旧的密码哈希算法预哈希其密码,然后使用此预哈希值代替他们的密码进行验证。之后(md5),重新计算新哈希并将新哈希存储在数据库中,在此过程中禁用legacy_password标志。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接