在 .NET 中使用 PBKDF2 和 nonce 进行 Web 身份验证的良好做法

Good practice web authentication with PBKDF2 and nonce in .NET

我正在构建 Web 应用程序,需要使用用户密码对用户进行身份验证。我正在尝试将其构建为 2021 年被认为是良好安全实践的标准。据我从我在网上阅读的内容中收集到的信息,从客户端发送密码到服务器通过 HTTPS(仅)。

[编辑:关于服务器的上下文] 在服务器端,我打算为每个用户存储一个盐和他们密码的散列版本。在网络上,我显然不应该发送明文密码,而且,为了防止回放,我也不应该发送散列密码值。因此,下面的客户端算法。 [结束编辑]

  1. 用户密码在客户端进行哈希处理[编辑:使用与服务器端相同的盐]。
  2. Nonce 在客户端生成[编辑:这应该是服务器生成并提供给客户端,见评论]
  3. 散列密码加上随机数在客户端散列。
  4. 随机数和最终哈希通过 HTTPS 从客户端发送到服务器。
  5. 一定要清除客户端上的密码(不是在我的代码示例中)。

这是我的实验示例代码:

public const int HASH_SIZE = 24; // size in bytes
public const int ITERATIONS = 100000; // number of pbkdf2 iterations
public const int NONCE_SIZE = 8; // size in bytes

public static string PasswordFlow(string userPassword, byte[] userSalt)
{
    // Hash the user password + user salt
    var hpwd = KeyDerivation.Pbkdf2(userPassword, userSalt, KeyDerivationPrf.HMACSHA512, ITERATIONS, HASH_SIZE);

    // Generate an 8 byte nonce using RNGCryptoServiceProvider
    RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
    byte[] nonce = new byte[NONCE_SIZE];
    rng.GetBytes(nonce);

    // Hash the hpwd byte[] converted to Base64 with the nonce byte array as salt
    var final = KeyDerivation.Pbkdf2(Convert.ToBase64String(hpwd), nonce, KeyDerivationPrf.HMACSHA512, ITERATIONS, HASH_SIZE);

    return Convert.ToBase64String(nonce)+"$"+ Convert.ToBase64String(final);
}

对于上述过程,我将不胜感激。我是否误解了它,搞砸了或错过了什么?我也在尝试理解:

我不是安全专家,我也是 C# 菜鸟,所以请原谅任何错误。

PBKDF2 旨在通过增加计算成本来减少暴力攻击。 它不是为了解决发送明文密码的问题 - 这应该通过其他安全机制来完成 - 安全通信(即 TLS 1.3)。

如果安全通信被破坏,那么无论您发送密码的明文还是散列都没有关系。

你所说的 NONCE 应该叫做 SALT。

基本上,PBKFD2:

  1. 获取您发送的任何数据(即密码)
  2. 加盐
  3. 应用 PRF(伪随机函数)次数
  4. Returns n 位派生密码

所以,回答你的问题:

  1. 可以 运行 PBKDF2 两次,但是我会增加迭代次数,而不是 运行 两次
  2. 100,000 是合理的迭代次数
  3. 24 字节(192 位)是合理的散列大小。尽管您使用 HMACSHA512 作为 PFR,它会生成大小为 512 位的散列。
  4. PBKDF2 标准允许 8 个字节的 SALT,但是 NIST 建议最少。 16 字节 - 我会增加 SALT 大小
  5. 如前所述,您可以在任何字符串输入上 运行 PBKDF2。在大多数情况下,它将是密码或密码