存储盐和哈希值是否可行?

25

我的理解是,盐并不需要保密,它只需要与任何中央标准不同即可,以便无法开发彩虹表或类似攻击来破解使用该算法的所有哈希值,因为盐可以破坏彩虹表。不过如果我有所错误,请指正。

在广泛使用的开源软件中,盐可能会被广为人知,这会使你面临攻击的风险,因为现在攻击者可以简单地攻击加过盐的哈希值,并创建包括盐数据的彩虹表。

我认为,有两种方法可以解决这个问题。第一种是在每个新版本的软件中更改盐,但这样做并不好,因为新版本的软件将不能再对旧密码哈希进行测试。

我想到的第二个解决方案是为每个密码存储一个盐;换句话说,每个密码都有一个不同的盐。缺点是必须以某种方式将盐与密码哈希关联起来,可能只需将其直接附在数据库中的密码旁边即可。也许使用用户名也可以(但这可能不行,因为用户名可能太短)。

我的问题是,这可行吗?将盐直接存储在其哈希密码旁边是否会有任何额外风险?在我看来,将盐存储在源代码中并没有什么不同,因此通过与密码一起存储盐并不会导致安全损失。

免责声明:我没有使用这个方案设计任何真实的安全系统。事实上,我从未设计过任何类型的密码系统。我只是让自己对安全问题保持模糊的了解。


今天有一篇很棒的文章讨论了这个问题,建议使用HMAC。文章链接:http://rdist.root.org/2009/10/29/stop-using-unsafe-keyed-hashes-use-hmac/。 - John Paulett
盐值加上HMAC无效的原因是什么? - Imagist
1
+1,如果可以的话,我会因为你的头像再给你一票的 :) - Mark Bell
请注意,本文不是关于存储密码的。它是关于HMAC的原始目的——验证消息(签名消息)。但当然,HMAC也是存储加盐密码的好选择。 - Lukáš Lalinský
但是为了让Imagist清楚,盐将成为HMAC函数的输入之一,因此HMAC并不意味着盐没有用处。 - Michael Burr
显示剩余4条评论
7个回答

20
更新: 使用一款优秀的库(例如Python中的passlib)。

这些库会生成一个每个密码独有的盐(salt),它们使用适当的哈希算法(仅使用SHA1等密码哈希算法不足以确保安全;必须以使其难以反转的方式应用,例如重复1000次以上循环等.)。这就是像bcrypt这样的密码哈希函数的工作原理。密码存储库会正确地完成所有这些步骤; 它们通常会产生一个包含分隔符的字符串,以便确定所使用的哈希系统和工作因子。你只需要存储该字符串,而不需要知道更多信息。


你可以将salt以明文形式存储在表格中。

  • 盐并不需要保密才能发挥作用。

  • 它只需要是随机的即可。

盐通过使哈希值与同一数据库中或其他数据库中的相同密码无法比较以及使大型预先生成的常见密码哈希查找列表(如“彩虹表”)失效来加强密码。

因此,关键在于盐对于每个用户都是唯一的、随机的值,应该与密码一起存储。问题中概述的替代方法(使用用户名作为盐,对整个应用程序使用单个盐值)都会失败:

  • 如果系统使用用户名或其他琐事作为盐,则密码可以与其他具有相同名称的用户在其他系统中进行比较(想象一下“管理员”或“root”用户帐户在不同系统中使用相同的密码的频率......)

  • 如果系统为同一系统中的所有用户使用单个随机盐,则两个偶然使用相同密码的用户将具有相同的哈希值,猜测一个用户的密码将轻松地破解另一个用户的密码。


正如我在其他评论中提到的那样,您需要为每个哈希选择一个唯一的盐。我无法看出随机化它有什么好处?关于您的第一个观点“如果系统使用用户名或其他琐事...”-如果对每个用户使用唯一的哈希(例如主键或用户名),那么这不是问题。 - Cocowalla
@Cocowalla "unique per user"=="每个用户唯一的盐值". 而你声称可以使用用户名,在我举的第一个失败点上已经失败了。 - Will
@Will:说得好 :)Michael Borgwardt 建议还可以使用系统中的某些独特内容——如果将其与每个用户的唯一哈希连接起来,那么就可以解决这个问题。 - Cocowalla
2
@Cocowalla:那么你只是把问题推到了一个暗角落,即如果用户被删除,然后重新创建的情况。这只是一个小问题,但确实存在。只需在密码数据库中存储一个随机值与密码哈希一起使用,然后就可以完成了!这就是那些阅读过Bruce的专业人士做事的方式 ;) - Will
1
@Will:好的,我承认了——使用随机盐并存储它似乎比使用现有数据更有好处。 - Cocowalla

10
尝试保密盐值是无意义的,因为盐值和哈希密码的整个实践只存在于我们从经验中得知即使我们无法完全可靠地保护数据库的情况下。你最多可以将盐值分别存储,并希望获取访问权限的攻击者找不到它,但是如果你使用了好的哈希算法并且足够长的独立盐值,你应该是安全的。
盐的目的仅在于确保你不能在整个数据库或甚至多个数据库上摊销暴力攻击的成本。
其中一种方法是每个新版本的软件都更改盐值,但这是无用的,因为新版本的软件将不能针对旧的密码哈希测试。
我看到的变化是在安装期间生成随机盐(当然要跨版本保留此盐),以便每个运行实例具有不同的盐。当然,为每个密码使用不同的盐(也许除了上述之外)会更好。

1
“盐的作用仅在于确保您无法将暴力攻击的成本分摊到整个数据库甚至多个数据库中。” - Jesse Hallam
我应该指出,这有时是与盐一起使用的:https://en.wikipedia.org/wiki/Pepper_(cryptography)。一个迭代是用应用程序密钥完成的。当仅泄漏数据库时,攻击者仍然必须猜测pepper。 - stewSquared

1

盐值在编程中的定义是必须随机才能发挥作用。不要使用任何确定性的值。这当然意味着您需要将其与哈希密码一起存储在数据库中。UNIX系统传统上甚至将哈希值存储在与密码相同的字段中(盐值是密码的固定长度前缀)。在数据库中,您可以在用户表中添加其他列。


4
我必须说我真的不太明白这个讨论。目前使用唯一随机盐是你能做的最好选择。我们可以争论它是否必要,但如果最佳选项也是最简单的选项,为什么不使用呢?生成一个随机数比在现有数据中找到足够好的密钥要难得多。 - Lukáš Lalinský
@Lukáš:请考虑,如果您生成一个随机盐,则必须能够在需要检查存储的哈希值时重新创建它。因此,该盐必须存储在某个地方或可派生。我认为最简单的选择是使用现有的唯一数据。 - Cocowalla
4
将随机数存储在数据库中肯定比确定要使用哪些好的现有数据更容易。您无需考虑它,它不会限制您将来修改现有数据的能力,它是一个完全独立的解决方案,只需要正常工作即可。如果因某种原因需要更改用于盐的现有数据(并且所有东西最终都需要更改),则不能仅重新计算密码的哈希值。 - Lukáš Lalinský
@Lukas 你可以把马带到水边,但你不能强迫它喝水。 - Will
如果最佳选择也是最简单的选择,为什么不直接使用它呢?这真是一句好引言。我要把它加入到我的列表中。 - Michael Burr
显示剩余7条评论

0

为每个密码生成唯一的盐是完全正常的。盐可以是现有材料(如UserID等)的产物,也可以是随机生成的。优点是,随着盐的强度增加,针对加密信息的攻击变得更加不切实际。

记住:每种加密算法都是可破解的。只有在破解保护(通过彩虹表或其他方式)比信息本身更有价值时,才能认为信息是“安全的”。

编辑:

假设您对密码学非常陌生,这里有一些提示:

  • 较长的盐比较短的盐更好。
  • 盐的可能值越多,越好。字母数字盐比数字盐更好。二进制盐比字母数字盐更好。
  • 盐不会减少针对单个密码的暴力攻击的可能性。

2
每个密码的盐值唯一是非常正常的,这基本上就是盐值的定义。如果每个密码的盐值都相同,那么盐值的作用就基本上被破坏了。 - Michael Burr
2
它并没有完全失去意义 - 对于整个数据库使用相同的盐仍然比根本不使用要好得多,因为虽然对于未加盐的MD5和SHA1有预先计算的彩虹表,但是对于足够长的盐则不会有。而且对于大多数攻击者来说,计算自己的彩虹表是不可行的(或者至少太昂贵了,不值得去尝试)。 - Michael Borgwardt
@Michael Borgwardt:没错,但是每个密码使用一个唯一的盐值仍然更好。这很容易实现,所以没有理由不这样做。 - Cocowalla

0

你的第二个解决方案“每个密码存储一个盐”是正确的,通常被使用。

“盐”主要是为了使检测到两个用户有相同密码变得困难 - 因此您需要将已知的“盐”混合到密码中。在密码检查时需要获取盐。

因此,通常要么生成一个随机盐并将其与密码一起存储,要么使用其他标识符(用户ID、用户名等)作为盐。


2
“Salt” 主要是用来使……我认为这是盐的次要好处。它的主要用途是降低成功攻击的概率。 - Jesse Hallam

0

在数据库中为所有密码使用单个盐值是有帮助的,但比为每个用户提供唯一盐值要不安全得多。

基本上:一个更长(以字节计)的密码+盐值会增加搜索空间,从而使使用“标准”彩虹表变得更加困难。

然而,如果所有条目都使用相同的盐,则可以创建一个彩虹表,专门用于攻击您的软件。如果您的用户群很大,那么有人可能会决定制作这样的彩虹表。

例如,如果您只是在每个密码后面添加“和大量盐”,然后进行哈希处理,攻击者可以构建一个由许多字符串生成的哈希值表,所有这些字符串都以“和大量盐”结尾。

因此,每个用户一个盐是最好的选择。但是,请记住,您还希望密码+盐值“长”一些。

如果您想使用主键,最好的方法可能是对主键进行哈希处理,而不是直接使用主键本身。因为如果用户43的密码+盐值看起来像“myPassword00000000043”,那么攻击者可以建立一个具有许多零的表格。创建时间戳和随机字符串可能更好,因为PKeys有时很容易被找到或猜测。
注意:我不是真正的加密专家,请勿在实际系统中使用此建议。

-1

实际上,您已经在用户表中存储了一个盐值:表的pkey。

您不必发明一个新列来存储盐。只需使用pkey即可。当然,这个想法假定您确实有与用户名关联的pkey。例如,用户名不是表中的pkey。

这是一个近似的重复 wtb: 密码哈希、盐和哈希值的存储


@Michael - 但正如我在对Lalinský答案的评论中提到的那样,那是不正确的 - 盐不需要是随机的,只要每个哈希都是独特的。 - Cocowalla
你怎么能建议别人读布鲁斯,却不理解当你可以访问的五个系统发生根目录EF45DC23E100CD25破解时它有多有用呢? - Will
@Will:一个我忘记考虑的有效观点 :) Michael Borgwardt建议使用系统中独特的东西来解决这个问题。我仍然推荐阅读应用密码学,只是现在还早,而且我读它已经有几年了 ;) - Cocowalla
1
@janis:你假设直接使用int pkey。这不是一个好的盐值。GUID是一个很好的盐值,而int可以通过一些派生方式(如哈希)或某些乘法方程使数字变大,从而用作“好”的盐值。 - Paul Sasik
1
GUID包含一个随机生成的元素。另一方面,通过哈希将某些东西扩展到更多位并不意味着它是一个好的排序。如果有40亿个潜在的输入值(例如32位),即使它们是100000000位或更长,仍然只有40亿个派生哈希。 - Will
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接