这种密码验证的实现看起来安全吗?

Does this implementation of password authentication seem secure?

我正在创建一个 Web 应用程序,我需要为其安全地执行身份验证。通过阅读大量文章和帖子,我对实施得出了以下结论。

  1. 前端密码哈希:使用 bcrypt,我将使用唯一的盐对纯文本密码进行哈希处理。此盐存储在每个用户的数据库中。这个#1 salted-hash 然后被发送到 API.

  2. 后端密码哈希:使用 PBKDF2,#1 salted-hash 再次用另一种独特的盐进行哈希处理,生成#2 salted-hash,然后用#2 salt 存储在数据库中。

因此,数据库总共有#2 salted-hash、#1 salt 和#2 salt。

因此,在进行授权时,#1 salt 然后用于散列创建 #1 salted-hash 的纯文本密码。然后转到 API 进行授权,在那里我们从数据库中获取 #2 salt 以创建 #2 salted-hash,然后将其与数据库中的 #2 salted-hash 进行比较以进行授权。

抱歉,如果这个问题看起来多余,但我找不到任何特定于实现的答案。如果有人能帮助我,那就太好了!

您看过信息安全堆栈交换吗?例如。 this answer.

客户端密码散列通常被认为是无用且不必要的步骤,不会提高安全性。事实上,您的实施甚至可能产生不必要的副作用,实际上会降低安全性:user enumeration

根据您在授权过程中如何实施第一步,您可能容易受到用户枚举的攻击。如果在您的数据库中找不到匹配的用户,我假设您不会发回#1 盐:

  1. Anon 尝试使用与您数据库中的用户不匹配的 username/email 登录
  2. 由于该用户不存在,因此没有对应的#1 salt发回
  3. 您的 API 没有发回盐,甚至更糟的是,发送了 "user not found" 响应。不管怎样,匿名现在知道这个 username/email 没有帐户

反之亦然:

  1. Anon 尝试使用 username/email 登录 您数据库中的用户
  2. 由于该用户确实存在,API 发回该用户的#1 salt
  3. Anon 现在知道这个 username/email 有一个帐户

为了避免这种用户枚举,如果没有找到匹配的用户,你的 API 应该发回 #1 salt even,更重要的是,它应该总是发送相同盐响应相同username/email,否则可能发生:

  1. Anon 尝试使用与您数据库中的用户不匹配的 username/email 登录
  2. 由于该用户不存在,因此没有对应的#1 salt发回
  3. 您的 API 发回随机盐
  4. Anon 第二次尝试 相同 username/email
  5. 您的 API 发回 另一个 与第一个不相同的随机盐
  6. Anon 可以推断出您的 API 合成了两种盐以试图规避用户枚举

要解决这个问题,您必须想出一种方法来始终发回相同的盐以响应尝试多次的未知 username/email。但它不应该对每个未知用户都是相同的盐,否则匿名会做出相同的推论。

总而言之,这似乎是不必要的复杂性。这可能是客户端密码散列不被认为是一件好事的原因之一。