关于c#:重新散列哈希(SHA512)

Rehashing a hash (SHA512)

我们正在进行标准的用户ID/密码检查。我们将哈希密码存储在数据库中。当用户输入凭据时,我们散列输入的密码,然后与数据库的密码进行比较。如果它们匹配,则对用户进行身份验证。

现在,负载测试下的这个登录过程大大减慢了,所以我被要求查看它。vs 2013 profiler指出散列方法是一个热门路径。看看有问题的方法,我们是在哈希过程中循环??

1
2
3
4
5
6
7
8
9
10
11
private const int totalHashCount = 1723;

public string CreateHash(string salt, string password, int securityIndex)
{
  string hashedPass = this.GenerateHashString(salt + password, securityIndex);
  for (int i = 1; i <= totalHashCount; i++)
  {
    hashedPass = this.GenerateHashString(hashedPass, securityIndex);
  }
  return hashedPass;
}

我去找开发人员,他说客户的安全团队希望我们重新刷新哈希表,并做一些大于1000的素数……他提供了电子邮件作为文档。

现在我不是一个密码专家,我们和客户关系很好,所以在我去找他们并将这个Rehash循环与他们的性能问题联系起来之前,我想看看这样的Rehash是否确实提高了安全性?

据我所知,一个哈希实际上是不可能反转的,那么为什么要浪费循环来重复这个过程呢?

思想?

编辑

新增发电灰:

1
2
3
4
5
6
7
8
9
10
protected internal string GenerateHashString(string textToHash, int securityIndex = 0)
{
   UnicodeEncoding uEncode = new UnicodeEncoding();
   SHA512Managed sha = new SHA512Managed();

   byte[] bytVal = uEncode.GetBytes(textToHash + hashIntPool[securityIndex].ToString());
   byte[] hashVal = sha.ComputeHash(bytVal);

   return Convert.ToBase64String(hashVal);
 }


重复散列操作对于保护密码身份验证是必要的,但是您做的不对,因此确实会浪费CPU而一无所获。

您应该使用像pbkdf2这样的算法,在每一轮哈希运算中都包含密码,以便保留密码的所有不可预测性。Bcrypt尤其是Scrypt也是不错的选择。

此外,一千回合还不够;为了防止离线字典攻击,即使在攻击者专用的密码测试硬件上执行哈希操作,您也需要使哈希操作相对缓慢。选择一个质数的回合是毫无意义的胡言乱语。轮数将取决于您选择的算法,但是对于带有sha-256的pbkdf2,大约10000到100000轮应该提供合理的安全级别。

为了防止获得哈希的攻击者快速尝试许多不同的密码,以查看哪个密码产生相同的哈希,需要使用慢速算法。确实,安全散列不可能反转,但它不会停止猜测,攻击者擅长将猜测的优先级设置为先尝试最可能的密码。哈希的重复就是提供这种必要的缓慢性的原因。

在StackOverflow上已经讨论过很多次了。我建议你参考前面的答案了解更多背景。

在C中,可以使用Rfc2898DeriveBytes安全地执行密码散列。您可以在base-64中对派生密钥进行编码,将其存储为字符串,或者实际使用它作为加密密钥来加密已知的纯文本,就像bcrypt算法一样。您会注意到Rfc2898DeriveBytes使用了一个"salt",我在别处讨论过;您需要将这个值与散列值一起存储,以便稍后执行身份验证。


这种被称为"拉伸"的重复散列技术被用来使暴力攻击更加困难。如果散列密码需要0.1秒(由于重复),那么攻击者最多可以每秒尝试10个密码来找到匹配的密码。如果您加快了散列过程,使它需要一微秒,那么攻击者可以每秒测试一百万个密码。

你需要平衡速度和安全。用户登录只需要足够快来满足用户的需求,因此0.1到0.5秒可能是可以接受的。

如果你的服务器超载,那么就买一个更快的处理器,或者买一个专用的哈希服务器。这比数据泄露的法律后果要便宜得多。