在 .NET 中使用 PBKDF2 和 nonce 进行 Web 身份验证的良好做法
Good practice web authentication with PBKDF2 and nonce in .NET
我正在构建 Web 应用程序,需要使用用户密码对用户进行身份验证。我正在尝试将其构建为 2021 年被认为是良好安全实践的标准。据我从我在网上阅读的内容中收集到的信息,从客户端发送密码到服务器通过 HTTPS(仅)。
[编辑:关于服务器的上下文]
在服务器端,我打算为每个用户存储一个盐和他们密码的散列版本。在网络上,我显然不应该发送明文密码,而且,为了防止回放,我也不应该发送散列密码值。因此,下面的客户端算法。
[结束编辑]
- 用户密码在客户端进行哈希处理[编辑:使用与服务器端相同的盐]。
- Nonce 在客户端生成[编辑:这应该是服务器生成并提供给客户端,见评论]
- 散列密码加上随机数在客户端散列。
- 随机数和最终哈希通过 HTTPS 从客户端发送到服务器。
- 一定要清除客户端上的密码(不是在我的代码示例中)。
这是我的实验示例代码:
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);
}
对于上述过程,我将不胜感激。我是否误解了它,搞砸了或错过了什么?我也在尝试理解:
- PBKDF2可以用两次吗?
- 100,000 次迭代是否是 PBKDF 迭代的合理大小?
- 24 字节是否是 PBKDF2 的合理哈希大小?
- 我认为 8 个字节对于 nonce(64 位数字)来说是一个合理的大小?
- PBKDF2 在 base64 和 nonce 中的哈希值有问题吗?运行 (它需要一个字符串输入)。
我不是安全专家,我也是 C# 菜鸟,所以请原谅任何错误。
PBKDF2 旨在通过增加计算成本来减少暴力攻击。
它不是为了解决发送明文密码的问题 - 这应该通过其他安全机制来完成 - 安全通信(即 TLS 1.3)。
如果安全通信被破坏,那么无论您发送密码的明文还是散列都没有关系。
你所说的 NONCE 应该叫做 SALT。
基本上,PBKFD2:
- 获取您发送的任何数据(即密码)
- 加盐
- 应用 PRF(伪随机函数)次数
- Returns n 位派生密码
所以,回答你的问题:
- 可以 运行 PBKDF2 两次,但是我会增加迭代次数,而不是 运行 两次
- 100,000 是合理的迭代次数
- 24 字节(192 位)是合理的散列大小。尽管您使用 HMACSHA512 作为 PFR,它会生成大小为 512 位的散列。
- PBKDF2 标准允许 8 个字节的 SALT,但是 NIST 建议最少。 16 字节 - 我会增加 SALT 大小
- 如前所述,您可以在任何字符串输入上 运行 PBKDF2。在大多数情况下,它将是密码或密码
我正在构建 Web 应用程序,需要使用用户密码对用户进行身份验证。我正在尝试将其构建为 2021 年被认为是良好安全实践的标准。据我从我在网上阅读的内容中收集到的信息,从客户端发送密码到服务器通过 HTTPS(仅)。
[编辑:关于服务器的上下文] 在服务器端,我打算为每个用户存储一个盐和他们密码的散列版本。在网络上,我显然不应该发送明文密码,而且,为了防止回放,我也不应该发送散列密码值。因此,下面的客户端算法。 [结束编辑]
- 用户密码在客户端进行哈希处理[编辑:使用与服务器端相同的盐]。
- Nonce 在客户端生成[编辑:这应该是服务器生成并提供给客户端,见评论]
- 散列密码加上随机数在客户端散列。
- 随机数和最终哈希通过 HTTPS 从客户端发送到服务器。
- 一定要清除客户端上的密码(不是在我的代码示例中)。
这是我的实验示例代码:
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);
}
对于上述过程,我将不胜感激。我是否误解了它,搞砸了或错过了什么?我也在尝试理解:
- PBKDF2可以用两次吗?
- 100,000 次迭代是否是 PBKDF 迭代的合理大小?
- 24 字节是否是 PBKDF2 的合理哈希大小?
- 我认为 8 个字节对于 nonce(64 位数字)来说是一个合理的大小?
- PBKDF2 在 base64 和 nonce 中的哈希值有问题吗?运行 (它需要一个字符串输入)。
我不是安全专家,我也是 C# 菜鸟,所以请原谅任何错误。
PBKDF2 旨在通过增加计算成本来减少暴力攻击。 它不是为了解决发送明文密码的问题 - 这应该通过其他安全机制来完成 - 安全通信(即 TLS 1.3)。
如果安全通信被破坏,那么无论您发送密码的明文还是散列都没有关系。
你所说的 NONCE 应该叫做 SALT。
基本上,PBKFD2:
- 获取您发送的任何数据(即密码)
- 加盐
- 应用 PRF(伪随机函数)次数
- Returns n 位派生密码
所以,回答你的问题:
- 可以 运行 PBKDF2 两次,但是我会增加迭代次数,而不是 运行 两次
- 100,000 是合理的迭代次数
- 24 字节(192 位)是合理的散列大小。尽管您使用 HMACSHA512 作为 PFR,它会生成大小为 512 位的散列。
- PBKDF2 标准允许 8 个字节的 SALT,但是 NIST 建议最少。 16 字节 - 我会增加 SALT 大小
- 如前所述,您可以在任何字符串输入上 运行 PBKDF2。在大多数情况下,它将是密码或密码