安全地存储密码,但确定相同的密码

Posted

技术标签:

【中文标题】安全地存储密码,但确定相同的密码【英文标题】:Store passwords safely but determine same passwords 【发布时间】:2018-08-18 18:19:24 【问题描述】:

我有旧版浏览器游戏,它在历史上使用简单的哈希函数来存储密码。我知道这远非理想。然而时间证明,大多数作弊者(多账户)对所有虚假账户都使用相同的密码。

在更新我的游戏时,我想更安全地存储密码。我已经知道,密码应该随机加盐,通过安全算法等进行哈希处理。这一切都很好。

但是有什么办法,如何正确存储密码并确定两个(或多个)用户使用相同的密码?我不想知道密码。我不想通过密码搜索。我只需要告诉你,可疑用户 A、B 和 C 使用的是同一个。

谢谢。

【问题讨论】:

【参考方案1】:

如果您正确存储它们 - 不会。这是正确存储密码的要点之一。

您可能有很长的密码,超出了彩虹表上可用的密码(不确定当前的技术状态,但它曾经是 10 或 12 个字符)并且不加盐。在这种情况下,两个密码将具有相同的哈希值。 这是一个非常糟糕的主意(但仍然是一个解决方案) - 如果您的密码泄露,有人可能会间接猜到它们(xkcd 参考)。

您也可以查看homomorphic encryption,但目前这是科幻小说领域。

【讨论】:

【参考方案2】:

好吧,如果你使用盐 + 散列,你所有的盐都是纯文本。当用户输入密码时,在存储/验证之前,您可以使用所有可用的盐对其进行哈希处理,并查看是否获得了相应的现有哈希。 :)

这样做的明显问题是,如果您使用 bcrypt 或 pbkdf2 正确地进行哈希处理,这将非常慢 - 这就是这些函数的重点。

我认为没有其他方法可以判断两个密码是否相同 - 您至少需要其中一个纯文本,只有当用户输入密码时。然后您想尽快将其从内存中删除,这与使用内存中的纯文本密码进行所有这些计算相矛盾。

【讨论】:

【参考方案3】:

这会在一定程度上降低所有密码的安全性,因为它会泄露有关两个用户何时拥有相同密码的信息。即便如此,这是一个可行的折衷方案,并且很容易在该限制内确保安全。

简短的回答是:对所有密码使用相同的盐,但要使该盐对您的网站唯一。

现在是长答案:

首先,描述处理密码的标准且适当的方法。之后我会为您解决差异。 (您可能已经知道所有这些,但值得重申。)

从一个不错的密钥扩展算法开始,例如 PBKDF2(还有其他的,有些甚至更好,但 PBKDF2 无处不在,足以满足大多数用途)。根据所涉及的客户端环境选择迭代次数。对于 javascript,您需要 1k-4k 迭代。对于数学速度更快的语言,您可以使用 10k-100k。

钥匙担架需要盐。一会儿我会谈谈盐。

客户端将密码发送到服务器。服务器应用快速散列(SHA-256 很好)并将其与存储的散列进行比较。 (对于设置密码,服务器做同样的事情;它接受 PBKDF2 哈希,应用 SHA-256,然后存储它。)

所有这些都是标准的东西。问题是盐。最好的盐是随机的,但对此没有好处。第二好的盐是从 service_id+user_id 构建的(即使用服务的唯一标识符并连接用户名)。这两者都确保每个用户的密码哈希都是唯一的,即使他们的密码相同。但你不希望这样。

所以现在终于到了你问题的核心。您想使用每个服务而不是每个用户的静态盐。所以像“com.example.mygreatapp”之类的东西(显然不要使用那个实际的字符串;使用基于您的应用程序的字符串)。使用恒定的盐,您服务上所有相同的密码将拉伸 (PBKDF2) 和散列 (SHA256) 到相同的值,您可以在不知道实际密码是什么的情况下比较它们。但是,如果您的密码数据库被盗,攻击者无法将其中的哈希值与其他站点数据库中的哈希值进行比较,即使他们使用相同的算法(因为它们的盐值不同)。

这种方案的缺点正是它的目标:如果您网站上的两个人拥有相同的密码,并且攻击者窃取了您的数据库并知道一个用户的密码,那么他们也知道另一个用户的密码。这就是权衡。

【讨论】:

以上是关于安全地存储密码,但确定相同的密码的主要内容,如果未能解决你的问题,请参考以下文章

Python 从数据库中存储和检索密码的最安全方法

为什么不安全地存储“安全问题”的答案?

如何在 Android 应用程序中安全地存储凭据(密码)?

如何安全地存储用户的密码?

确定安全威胁与漏洞-C

局域网共享账号密码错误怎么办