公开暴露盐分是否可以接受?

Is it acceptable to publicly expose salts?

我正在为移动应用构建 REST API。我需要处理身份验证。据我所知,这些是有关密码存储的最佳做法。

客户端-注册

  1. 让用户选择密码
  2. 从强随机数生成器创建足够长的随机盐
  3. 将盐附加到密码并对其进行哈希处理。
  4. 将哈希和盐发送到服务器并将其存储在数据库中

服务器端登录

  1. 公开一种方法,该方法 returns 用户对给定用户 ID(电子邮件)的加盐

客户端登录

  1. 用户输入他的用户名和密码
  2. 检索给定用户标识的盐
  3. 将盐附加到密码并对其进行哈希处理。
  4. 将哈希发送到服务器,检查它是否相同并验证用户

显然,这种方法的弱点在于攻击者可以毫不费力地看到任何用户的盐分。这个可以吗?显然我也可以将盐存储在客户端,但如果例如用户正在从不同的 phone 登录,他必须能够检索盐。

替代方法是以明文形式发送密码,并在服务器端处理所有加盐和哈希处理。我会使用 HTTPS。

以下哪种方法更可取?我还缺第三个吗?

Obviously the week point in this approach is that an attacker can see the salt for any user effortlessly.

不,弱点是你的服务器信任客户端。以下是 MSDN 对这种情况的简要说明:

You should never trust user input directly, especially if the user input is anonymous. Remember the two golden rules: never trust user input, and always check data as it moves from an untrusted to a trusted domain.

来源:Do Not Trust User Input Directly

在这里,您有用户数据从他们不受信任的客户端移动到您的服务器。您需要在您的服务器上验证他们的输入。没有任何攻击可以通过在客户端执行盐的生成和密码的散列来防止。确保服务器-客户端通信安全的唯一方法是使用用于非对称加密的安全握手密钥交换。这发生在 HTTPS 上,一旦 link 是安全的,那么以明文形式发送密码的危险性不亚于将代码发送到客户端以执行客户端散列。

如果您不使用 HTTPS 保护与客户端的连接,您总是容易受到中间人攻击。即使密码在返回到您的服务器时名义上是模糊的,攻击者也可以冒充您的服务器到客户端,发送一个跳过哈希的 HTML 表单或以明文形式将密码也发送到他们的服务器。

在您的场景中暴露盐分 一个额外的安全漏洞,但与允许攻击者直接获取密码的明显漏洞相比,它是次要的。如果您愿意,我可以进一步解释这种攻击,但除​​非密码本身得到更好的保护,否则这是一种完全没有意义的攻击;一旦发生这种情况,您就无需首先暴露盐。


回应评论,如果您确定盐生成正确,确实可以在不降低安全性的情况下公开盐。对于盐,这意味着它们足够长,足够随机,并且在某些方案中它们是 cryptographically secure. The problem is that you allow the client to generate the salt, which means, I think we all agree now, that they can't be trusted. Maybe an attacker corrupted an external library you're relying on for PRNG. Maybe some user devices have an insufficient entropy pool. Maybe their device isn't secure against side-channel attacks。如果您正在制作 Android 应用程序,肯定会有一些非常不安全的设备。在 Apple 的围墙花园中,这些风险可能很低,但您永远无法消除它们。

然而,就像我最初所说的那样,与您的计划的真正危险相比,这些风险微不足道。也就是说,您正在将明文传递的散列传输到您的服务器。即使您在将其存储到数据库之前进一步散列它,因此 SQL 注入不能只转储所有用户密码,您仍然容易受到中间人攻击。未加密的 HTTP 流量可以被它经过的任何路由器观察和记录。一旦攻击者观察到哈希密码,他们就可以向您的服务器重新发送相同的请求,并进行错误的身份验证。

你应该严格选择替代方案,"to send the password in plaintext and handle all the salting and hashing on the server side. I would use HTTPS."

当然,上面这句话是高度矛盾的:如果你使用 HTTPS,那么密码就不会是明文的。您应该 使用 HTTPS。

在服务器端,您可以将随机生成的盐以明文形式存储在数据库中。您不应选择普通散列,而应选择密码散列(例如由 PBKDF2 生成的散列)。

如果您的数据库被盗,盐就在那里:盐将确保相同的密码不会被泄露为相同的。此外,盐会使 rainbow tables 与预先计算的密码哈希值无法使用。

需要注意两件重要的事情:对于 Web 客户端,您必须依赖受信任的 TLS 证书存储来创建安全的 TLS 连接。此外,您的客户可能无法访问好的随机数生成器。