每个用户的随机盐可以用迭代散列替换吗?

时间:2010-04-29 10:23:08

标签: security authentication encryption cryptography

在构建我希望的是一个正确构建的身份验证机制的过程中,我遇到了很多指定的内容:

  1. 用户密码必须加盐
  2. 使用的盐应该足够随机并按用户生成
  3. ...因此,必须将盐与用户记录一起存储,以支持验证用户密码
  4. 我完全同意第一点和第二点,但似乎后者有一个简单的解决方法。而不是相当于(伪代码在这里):

    salt = random();
    hashedPassword = hash(salt . password);
    storeUserRecord(username, hashedPassword, salt);
    

    为什么不使用用户名的哈希作为盐?这产生了一个分布均匀(大致)随机的盐的区域,并且每个单独的盐与盐功能所提供的一样复杂。更好的是,您不必将salt存储在数据库中 - 只需在身份验证时重新生成它。更多伪代码:

    salt = hash(username);
    hashedPassword = hash(salt . password);
    storeUserRecord(username, hashedPassword);
    

    (当然,上面示例中的hash应该是合理的,比如SHA-512或其他强哈希。)

    这对我来说似乎是合情合理的,因为我对密码的了解很少(但很少),但事实上这是对广泛推荐的练习的简化,这让我想知道是否有一些明显的原因让我误入歧途我不知道

    编辑有些人似乎不知道问题是什么。我无法建议不使用盐。参考TheRook的编辑答案:我熟悉那些CWE中提到的参考文献。我的核心问题是:为什么hash(用户名)是可预测的盐?

    编辑2 感谢所有提供答案的人; biffabacon在他的第2段直接解决了我的核心问题(基本上,你可以做的任何事情,以最大限度地使用盐的域,因此生成的哈希密码是好的),但在这个问题的各种评论中有很多美味的信息。

6 个答案:

答案 0 :(得分:7)

盐的原因是为了防止密码分析攻击。每个用户的唯一盐意味着您无法判断两个用户是否具有相同的密码。每个用户的非确定性盐意味着您无法判断两个系统上是否使用了相同的用户名:密码。

不要试图让盐更加完美。如果你不喜欢这个空间,那么就不要使用它们,并努力直接保护你的数据(和备份!)。

答案 1 :(得分:6)

盐有助于使用预计算或字典攻击防御攻击者。使用salt时,攻击者需要为每个salt值创建一个单独的字典。但是,如果盐不是随机的,则会给攻击者一个优势,因为他们可以创建比其他人更可能的词典。例如,他们可以使用jsmith的盐(或jsmith的哈希)创建一个字典。出于这个原因,盐通常是随机的好主意。

将预计算攻击与哈希(用户名)盐和随机盐进行比较:例如,攻击者决定为最常见的1000个用户名创建词典,比如说,打造六个不同的哈希算法;这是6000盐和6000字典。如果使用随机32位盐,则为2 ^ 32或大约42亿个字典。因此,当盐空间大幅减少时(就像使用哈希(用户名)一样)预先计算的攻击变得更加可行

答案 2 :(得分:5)

盐的目的是防止攻击者进行并行攻击。必须在空间和时间上理解这种并行性;粗略地说,这意味着在两次或更多次攻击之间分担攻击成本。

例如,考虑非盐渍哈希密码设置。攻击者可以对字典中的所有单词进行散列,以获得与字典大小成比例的成本,并检查那些与几个散列密码相关的散列单词。这可以是同时的(攻击者有一个散列密码列表并希望破解一个)或迭代(攻击者预先计算他的散列字典,然后将其用作针对不同系统中的多个密码的工具)。无论哪种方式,这都是成本分摊。

salt 是一些数据,对于每个散列密码实例而言应该是有些独特的。盐析可以防止这种成本分担,达到盐的独特程度。

使用用户名(或其哈希值)作为salt利用用户名唯一性:通常,在给定时间给定系统,用户名是唯一的。这可以防止本地空间共享:如果攻击者获得所有哈希密码的快照,他就无法与成本共享并行攻击它们;他将不得不为每个受攻击的密码产生哈希字典费用。但是,这并不妨碍时间共享(攻击者使用对应于用户“bob”的盐预先计算散列字典,并且会定期尝试猜测Bob的密码,假设Bob定期更改其密码,例如因为这是由他的系统管理员强制执行)。这并不能阻止某些全局共享(有几个 - 很多 - 系统,用户名为“bob”)。

因此使用用户名作为盐并不错;这比不使用盐更好。但随机盐仍然更好,因为即使在用户名保持不变的情况下(用户更改其密码;不同系统上具有相同名称的两个用户),它也会发生变化。

答案 3 :(得分:1)

如果你使用随机盐,你就会从一个非常大的,随机的可能性池中拉出来。当你从用户名中提取时,你就会从一个大的池中拉出来,因为用户名通常都是小写的,字典单词',并且是OS /身份验证系统对用户名的约束主题(必须以字母开头,没有特殊字符,某些操作系统仍需要最多8个字符用户名)。很多用户名是标准化的,或者至少是普及的:root,administrator,bob,mary ......你明白了。 另一个问题是用户名通常不受保护,你可以通过apache的用户目录看到它们,匿名ftp经常允许公共目录按用户名等分组。攻击者可以从收获用户名开始,然后构建自己非常好的盐列表。

所有这些都会增加一个问题:提出有效的盐列表的可能性更高。

这使攻击者能够离线预先计算用户名及其可能的哈希值,并将其设置为暴力攻击。您可能希望创建一个挑战 - 响应机制来阻止它。

答案 4 :(得分:1)

有两个与盐的使用有关的公认漏洞。第一个是CWE-759,它指出salt 必须用于密码。第二个vulenrablity更重要,它是CWE-760:使用带有可预测盐的单向哈希。根据CWE-760,

是您的目标腌制机制

应使用大Cryptographically secure pseudo-random number生成盐。这个数字应该是base256。良好的大小将与消息摘要产生的位数相同。例如,SHA256应具有256位盐。两者都应具有相同的熵量,因为它们都易受蛮力影响。

为了打破这种大小的盐,你需要一张如此之大的彩虹表,我们甚至没有一句话。

  

为什么不使用用户名的哈希值   作为盐?

在检索salt之前,无法破解密码哈希。盐使预先计算的攻击更加耗费资源,但绝不可能。使用用户名哈希的问题是2倍。首先,您正在计算2个消息摘要,这是浪费资源。从安全角度来看,这种腌制机制是不合适的,因为Web应用程序的用户名通常是公共知识。盐必须是秘密,理想情况下,这个秘密与密码哈希分开存储。如果salt与密码哈希一起存储在数据库中,则可以使用SQL Injeciton获取这两个值,然后可以使用简单的字典攻击来中断哈希。

为了改善salt提供的安全性,它应该与密码哈希分开存储,以便在任何哈希都被破坏之前必须妥协。这可以通过将盐存储在单独的数据库中或本地平面文件中来实现。请记住,mysql的file_priv可以用来读取这个平面文件,所以请确保禁用它。

答案 5 :(得分:0)

我将采取略微相反的观点。由于给出的原因(但总比没有好),纯哈希不是最好的想法,但我认为如果你做一个应用程序范围的salt +用户名的哈希是一个完全不同的故事。它仍然是每个用户,但不是攻击者可以轻易猜到的东西。显然,您需要确保攻击者看不到应用程序范围的盐,例如,通过从应用程序服务器目录树之外的文件中读取它。

您可以使用多个哈希值进一步扩展此值。也就是说,不要只使用

  H(password.username.appsalt)

使用类似

的内容
  h1 = H(password.appsalt1.username.password)
  h2 = H(password.appsalt2.username.password)
  h3 = H(password.appsalt3.username.password)
  H(h1.H(h2.H(h3))))

(其中H()是散列,'。'是简单连接。)额外的时间不会对您的应用程序产生明显的影响,但会使攻击者的成本更高。

我认为即使在用户身份验证表中存储随机哈希值,多个哈希值也是一个好主意。同样,它不会对您的应用程序产生明显影响,但会使攻击者更加困难。