我们在网站中引入了密码加密。盐的计算如下:
Rfc2898DeriveBytes hasher = new Rfc2898DeriveBytes(Username.ToLowerInvariant(),
System.Text.Encoding.Default.GetBytes("Wn.,G38uI{~6y8G-FA4);UD~7u75%6"), 10000);
string salt = Convert.ToBase64String(hasher.GetBytes(25));
对于大多数用户名来说,盐总是一样的。但对于某些用户名,它在每次调用时都会发生变化。有人能告诉我我们做错了什么吗?
假设您也在使用RFC2898DeriveBytes对密码本身进行散列,那么@CodesInChaos是正确的,那么您做错的是:
- 基于用户名构建salt,而不是使用加密PRNG为每个用户生成新的salt。
- 你应该使用类似的东西。NET RNGCryptoServiceProvider类生成8到16(二进制)字节的随机salt
- 例如,来自Rfc2898DeriveBytes示例1
- 你应该使用类似的东西。NET RNGCryptoServiceProvider类生成8到16(二进制)字节的随机salt
byte[] salt1 = new byte[8];
using (RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider())
{
// Fill the array with a random value.
rngCsp.GetBytes(salt1);
}
salt应该与密码哈希和迭代次数一起存储在数据库的clear中(这样你就可以更改它),可能还有一个版本代码(这样你可以再次更改它,即你当前计算的salt方法是版本1,随机salt是版本2)。
- 在salt上花费20000次PBKDF2迭代,而不是花费在实际的密码哈希上
- 由于RFC2898DeriveBytes是PBKDF2-HMAC-SHA-1,并且SHA-1具有本地20字节输出,因此前20字节的10000次迭代
- 在接下来的20个字节中再进行10000次迭代,然后将其截断为仅需要5次才能得到25个字节的输出
- 这是一个弱点,因为防御者每次登录都必须花时间在salt上,无论是花在salt还是密码哈希上。攻击者必须为每个用户名花费一次时间,然后他们将存储结果并尝试_illions(其中_非常大)的密码猜测
- 因此,攻击者具有比正常情况更大的边际优势,因为他们可以预先计算salt,而您必须实时计算
如果您没有使用RFC2898DeriveBytes、另一个PBKDF2实现、BCrypt或SCrypt来进行实际的密码哈希,那么这就是您做错的地方。
在某些情况下(但不是所有情况下)修剪用户名完全是偶然的;只要确保在对密码进行哈希处理之前不要对其进行修剪即可。