为哈希隐藏盐的必要性

Posted

技术标签:

【中文标题】为哈希隐藏盐的必要性【英文标题】:The necessity of hiding the salt for a hash 【发布时间】:2010-09-17 19:44:06 【问题描述】:

在工作中,我们有两种相互竞争的盐理论。我工作的产品使用用户名或电话号码之类的东西来加盐。本质上,对于每个用户来说都是不同的,但我们很容易获得。另一款产品为每个用户随机生成一个salt,并在用户每次更改密码时更改。然后盐在数据库中被加密。

我的问题是第二种方法是否真的有必要?我可以从纯理论的角度理解它比第一种方法更安全,但从实用性的角度来看呢。现在要对用户进行身份验证,salt 必须未加密并应用于登录信息。

经过考虑,我只是没有看到这种方法真正的安全收益。将盐从一个帐户更改为另一个帐户,即使攻击者知道如何快速确定每个帐户的值,仍然使某人尝试暴力破解散列算法非常困难。这是在假设密码足够强的情况下进行的。 (显然,找到一组密码的正确哈希值,其中它们都是两位数字,比找到正确的 8 位密码哈希值要容易得多)。我的逻辑是不正确的,还是我遗漏了什么?

编辑:好的,这就是为什么我认为加密盐确实没有实际意义的原因。 (让我知道我是否走在正确的轨道上)。

对于下面的解释,我们假设密码总是 8 个字符,salt 是 5,所有密码都由小写字母组成(这只是为了让数学更容易)。

为每个条目使用不同的盐意味着我不能使用相同的彩虹表(实际上,如果我有一个足够大的大小,我可以使用,但我们暂时忽略它)。据我了解,这是真正的关键,因为要破解每个帐户,我必须重新发明***,可以说每个帐户。现在,如果我知道如何将正确的盐应用于密码以生成散列,我会这样做,因为盐实际上只是扩展了散列短语的长度/复杂性。所以我会减少我需要生成的可能组合的数量,以“知道”我的密码+盐从 13^26 到 8^26,因为我知道盐是什么。现在这使它变得更容易,但仍然非常困难。

所以开始加密盐。如果我知道盐是加密的,我不会先尝试解密(假设我知道它有足够的加密级别)。我会忽略它。而不是试图弄清楚如何解密它,回到前面的例子,我只生成一个更大的彩虹表,其中包含 13^26 的所有密钥。不知道盐肯定会减慢我的速度,但我认为它不会增加尝试首先破解盐加密的艰巨任务。这就是为什么我认为这不值得。想法?

这里是一个链接,描述了密码在暴力攻击下可以保存多长时间: http://www.lockdown.co.uk/?pg=combi

【问题讨论】:

问得好,凯文,非常及时。我无法对安全性发表评论,但所有这些加密和解密都会对性能造成影响,这是肯定的。 你不需要忽略大小合适的彩虹表——该表也使得加密的盐没有实际意义,因为它可以解密加盐的散列并再次用于解密原始散列。好消息是创建这张桌子可能需要几个世纪的时间。 让我印象深刻的是,我不完全确定这样大小的彩虹桌需要那么长时间才能制作出来。他是一个古怪的想法,使用像 Kraken 这样的分布式计算平台(真的就是这样)来生成它。那需要多长时间? @Kevin:SHA1 返回一个 40 个字符的十六进制字符串,因此有 40^16 个可能的返回值。假设一台计算机每秒可以计算 1000 个哈希,我计算出 100 万台计算机需要大约 100 亿年才能生成能够确定该大小字符串的彩虹表。 @tloach 有软件可以做峰值 5600M/s MD5 哈希和 2300M/s SHA1 哈希。 golubev.com/hashgpu.htm 【参考方案1】:

我对“salt”的理解是它使破解更加困难,但它并没有试图隐藏额外的数据。如果您想通过使盐“保密”来获得更高的安全性,那么您真的只需要在您的加密密钥中添加更多位。

【讨论】:

【参考方案2】:

有两种技术,具有不同的目标:

“salt”用于使两个原本相等的密码以不同的方式加密。这样一来,入侵者就无法有效地对整个加密密码列表使用字典攻击。

(共享的)“秘密”是在对消息进行散列之前添加的,因此入侵者无法创建自己的消息并让它们被接受。

【讨论】:

【参考方案3】:

这里的答案是问问自己,你真正想要保护的是什么?如果有人可以访问您的数据库,那么他们就可以访问加密的盐,并且他们可能也可以访问您的代码。有了这些,他们可以解密加密的盐吗?如果是这样,那么无论如何加密几乎没有用。盐确实可以做到这一点,因此如果它被入侵,就不可能形成一个彩虹表来一次性破解您的整个密码数据库。从这个角度来看,只要每个盐都是唯一的,就没有区别,那么您的盐或每个密码的加密盐都需要单独进行暴力攻击。

【讨论】:

对这些概念有点陌生。因此,这可能看起来像一个幼稚的问题,但是如果您只为每个可能的密码尝试一种盐值组合,那么在知道每个密码的盐值的情况下形成彩虹表会不会更容易? ranbow table 来自 1000 个常用密码,其破解速度几乎与散列相同。唯一可行的方法是,如果您假设密码的分布是统一的.. 我们知道它们不是 不,他们不太可能同时访问数据库和源代码,并且源代码不应包含用于加密的应用程序机密。考虑 SQL 注入的攻击向量,或暴露的备份数据库。那些不授予对代码或加密密钥的访问权限。【参考方案4】:

第二种方法只是稍微安全一点。盐可以保护用户免受字典攻击和彩虹表攻击。它们使雄心勃勃的攻击者更难破坏您的整个系统,但仍然容易受到针对您系统的一个用户的攻击。如果您使用公开可用的信息,例如电话号码,并且攻击者会意识到这一点,那么您已经为他们的攻击节省了一步。当然,如果攻击者获取了您的整个数据库、盐和所有内容,这个问题就没有实际意义了。

编辑:在重新阅读了这个答案和一些 cmets 之后,我突然想到,一些混淆可能是由于我只是比较了两个非常具体的问题中提出的案例:随机盐与非随机盐。如果攻击者获取了您的整个数据库,使用电话号码作为盐的问题是没有实际意义的,根本不是使用盐的问题。

【讨论】:

它们如何防止字典攻击? 通过强制攻击者为每个密码+盐组合创建一个新字典。没有盐,一个字典可以用于你的整个密码表。 “当然,如果攻击者得到你的整个数据库、盐和所有东西,这个问题就没有实际意义了。”这不是盐被加密的原因吗? @Bill:你刚刚描述了一张彩虹桌。字典攻击是我使用普通单词并尝试对其进行身份验证的地方。这是一种蛮力,答案空间大大减少。没有哈希可以抵御暴力破解。 我从未听说过这种做法,但我不是安全专家。似乎加密独特的盐会增加一层额外的保护,防止彩虹表攻击。【参考方案5】:

这是一个简单的例子,说明为什么每个哈希使用相同的盐是不好的

考虑下表

UserId  UserName,   Password
     1  Fred       Hash1 =  Sha(Salt1+Password1)    
     2  Ted        Hash2 =  Sha(Salt2+Password2)    

salt 1与salt2相同的情况1 如果将 Hash2 替换为 Hash1 则用户 2 可以使用用户 1 的密码登录

情况2当salt 1不一样salt2 如果 Hash2 替换为 Hash1 则 user2 无法使用用户 1 的密码登录。

【讨论】:

我们没有为每个帐户使用相同的哈希值,而是为每个哈希值使用相同类型的信息。例如,帐户的盐是帐户的电话号码等。 我知道这是很久以后的事了,但是......你不能依靠盐来抵御这种类型的攻击。我们必须假设攻击者知道除了秘密(即密码)之外的有关身份验证系统的所有信息。因此,我们必须假设攻击者知道 a)我们使用什么作为盐(大多数情况下它以明文形式存储在同一个数据库和表中)和 b)我们如何使用盐进行散列(即附加,散列两次,等等)。如果用户有能力切换哈希,那么我们必须假设他们也可以切换盐。盐在这里无济于事。【参考方案6】:

真的,这取决于您尝试保护数据的攻击类型。

为每个密码设置唯一盐的目的是防止对整个密码数据库的字典攻击。

为每个密码加密唯一的盐将使破解单个密码变得更加困难,是的,但您必须权衡是否真的有很多好处。如果攻击者通过蛮力发现这个字符串:

Marianne2ae85fb5d

散列到存储在数据库中的散列,真的很难弄清楚哪一部分是pass,哪一部分是salt?

【讨论】:

如果有人暴力破解了这样一个不可能猜到的密码,我会说你无论如何都被水洗了,因为显然他们拥有没人想到的技术。【参考方案7】:

隐藏盐是不必要的。

应该为每个哈希使用不同的盐。在实践中,这很容易通过从加密质量随机数生成器中获取 8 个或更多字节来实现。

来自previous answer of mine:

Salt 有助于阻止预先计算的字典攻击。

假设攻击者有一个可能的密码列表。他可以散列每个 并将其与受害者密码的哈希值进行比较,看看是否 火柴。如果列表很大,这可能需要很长时间。他不 想在下一个目标上花那么多时间,所以他记录了结果 在“字典”中,哈希指向其相应的输入。如果 密码列表非常非常长,他可以使用像 彩虹表以节省一些空间。

但是,假设他的下一个目标加盐了他们的密码。即使 攻击者知道盐是什么,他的预计算表是 毫无价值——盐改变了每个密码产生的哈希值。他 必须重新散列他列表中的所有密码,并附上目标的 盐输入。每种不同的盐需要不同的 字典,如果使用了足够的盐,攻击者将没有空间 为他们存储字典。交易空间节省时间是没有 更长的选择;攻击者必须回退到散列每个密码 在他想要攻击的每个目标的列表中。

因此,没有必要对盐保密。确保 攻击者没有与之对应的预先计算的字典 特定的盐就足够了。


在考虑了更多之后,我意识到欺骗自己认为盐可以隐藏是危险的。最好假设盐不能被隐藏,尽管如此,将系统设计为安全的。我提供更详细的解释in another answer.


但是,NIST 最近的建议鼓励使用额外的、 秘密“盐”(我看到其他人称这个额外的秘密“胡椒”)。可以使用这个秘密作为盐来执行密钥推导的另一个迭代。这一轮不是增加对预先计算的查找攻击的强度,而是防止密码猜测,就像一个好的密钥派生函数中的大量迭代一样。如果与散列密码一起存储,此机密将毫无用处;它必须作为秘密进行管理,这在大型用户数据库中可能很困难。

【讨论】:

隐藏盐是必要的,因为在获得哈希之前无法破坏它。这与 CWE-760 (cwe.mitre.org/data/definitions/760.html) 相关 @The Rook - 不,你误解了这个问题。它指的是使用可预测的盐。它没有说要保守盐的秘密。确实,如果不使用另一个秘密,你就不能真正保持盐的秘密……在这种情况下,你为什么不使用第二个秘密作为盐呢?将用户名用作盐是不好的,因为多个系统可能共享相同的用户名,因此为该特定盐构建表更值得。随机盐是最好的,但它们不需要需要保密。 参见:***.com/questions/1645161/… @erickson,我认为您在这里混淆了术语。盐不会阻止字典攻击,除非它们被隐藏。很多盐都是公开储存的。而且,如果您知道盐,那么您的字典攻击只会在将盐附加到您的字典术语时减慢:) 盐可以防止 Rainbow 表查找.... 这非常快。如果攻击者知道你的盐,你就不会受到字典攻击的保护。如果攻击者不知道你的盐,你可以免受字典攻击,他们必须使用暴力攻击。 @Ultratrunks 是的,我已经澄清了我在这个主题上的一些答案,使用术语“预计算字典攻击”。比 Rainbow 表更通用,它指的是允许使用其哈希作为键反向查找明文的任何结构。无论您选择何种术语,我都明确解释说 salt 旨在击败 Rainbow 表和类似机制。您应该始终假设攻击者知道盐。如果可以保密,您就不需要对密码进行哈希处理。【参考方案8】:

... 用户名或电话号码之类的东西来加盐哈希。 ...

我的问题是第二种方法是否真的有必要?我可以从纯理论的角度理解它比第一种方法更安全,但是从实用性的角度来看呢?

从实用的角度来看,salt 是一个实现细节。如果您改变了收集或维护用户信息的方式——使用您的确切示例,用户名和电话号码有时会发生变化——那么您可能已经损害了您的安全性。您是否希望这种面向外部的更改具有更深层次的安全问题?

停止要求每个帐户都有一个电话号码是否需要进行完整的安全审查,以确保您没有打开这些帐户导致安全漏洞?

【讨论】:

更不用说您是否使用了一些任意位的用户信息,当他们更改该信息时,您需要他们再次输入密码,以便您可以使用新的 salt 再次对其进行加密。【参考方案9】:

隐藏的盐不再是盐。是辣椒。它有它的用途。它不同于盐。

Pepper 是添加到密码 + salt 中的密钥,它使散列成为 HMAC(基于散列的消息验证码)。有权访问散列输出和盐的黑客理论上可以暴力猜测将生成散列的输入(并因此在密码文本框中通过验证)。通过添加胡椒,您可以以加密随机方式增加问题空间,从而在没有严重硬件的情况下使问题变得难以解决。

有关胡椒的更多信息,请查看here。

另见hmac。

【讨论】:

【参考方案10】:

我倾向于隐藏盐。我使用 10 位盐,方法是在对密码进行散列之前将 1 到 1024 的随机数添加到密码的开头。在将用户输入的密码与哈希值进行比较时,我从 1 循环到 1024 并尝试所有可能的 salt 值,直到找到匹配项。这需要不到 1/10 秒的时间。我从 php password_hash 和 password_verify 得到了这样做的想法。在我的示例中,10 位盐的“成本”是 10。或者从另一个用户所说的,隐藏的“盐”被称为“胡椒”。盐在数据库中未加密。被强行逼出来了这将使彩虹表需要将散列反转 1000 倍。我使用 sha256 是因为它速度快,但仍然被认为是安全的。

【讨论】:

因此,如果我的密码是“345678”,而您添加的随机盐恰好是“12”。如果有人输入“45678”作为我的密码。这在很多方面似乎都是错误的。

以上是关于为哈希隐藏盐的必要性的主要内容,如果未能解决你的问题,请参考以下文章

C# 保持哈希盐的安全,即使是来自开发人员

如何在 Python 中使用 scrypt 生成密码和盐的哈希值

用于密码盐的 Python3 os.urandom()

针对 Scrypt 组合哈希验证 python 密码:(设置+盐+哈希)

在javascript中隐藏我的哈希号

Perl,将数字键哈希转换为数组