更新:我最近从这个问题中了解到,在下面的整个讨论中,我(我确信其他人也这么做了)有点困惑:我一直称之为彩虹表的,实际上是一个哈希表。彩虹表是更复杂的生物,实际上是地狱人散列链的变体。虽然我相信答案仍然是一样的(因为它不归结于密码分析),但有些讨论可能有点偏颇。问题:"彩虹桌是什么?它们是如何使用的?"
通常,我总是建议使用加密的强随机值作为salt,用于哈希函数(例如用于密码),例如防止彩虹表攻击。
但是,盐在密码学上是随机的吗?在这方面,任何唯一值(每个用户唯一,例如userid)是否足够?它实际上可以防止使用一个彩虹表破解系统中的所有(或大多数)密码…但是,缺乏熵真的削弱了哈希函数的加密强度吗?
注意,我不会问为什么要使用salt,如何保护它(不需要),使用一个常量散列(不需要),或者使用什么类型的散列函数。盐是否需要熵。
感谢所有的答案,但我想集中在我(一点)不太熟悉的领域。主要是对密码分析的影响-如果有人从密码数学POV输入信息,我会非常感激。此外,如果还有其他未考虑的向量,那么这也是很好的输入(请参见多个系统上的@dave sherohman point)。除此之外,如果你有任何理论、想法或最佳实践-请用证据、攻击场景或经验证据来支持这一点。甚至是对可接受的权衡的有效考虑…我熟悉这个主题的最佳实践(Capital B Capital P),我想证明这实际上提供了什么价值。
编辑:这里有一些很好的答案,但我认为正如@dave所说,它可以归结为彩虹表中的常用用户名…还有可能不太常见的名字。但是,如果我的用户名是全局唯一的呢?对于我的系统不一定是唯一的,但针对每个用户-例如电子邮件地址。没有为单个用户构建RT的动机(正如@dave强调的那样,salt不是保密的),这仍然会阻止集群。唯一的问题是,我可能在不同的网站上有相同的电子邮件和密码,但是Salt无论如何也不能阻止。那么,归根结底就是密码分析——熵是否必要?(我目前的想法是,从密码分析的角度来看,这是不必要的,但从其他实际的原因来看。)
- 我必须说我对下面的许多答案感到困惑。使用salt的主要目的是简单地防止彩虹表攻击,因此用户应该做任何独特的事情,因为它迫使攻击者为每个salt重新创建彩虹表。我的2C。
- 如果salt对于例如站点是必需的,那么通常在URL中有ID。如果攻击者知道(我们假设他知道)密码salt是userid+username,那么修改攻击是相当容易的,以避免salt值。
- @dmajkic,salt旨在防止RT攻击,并区分不同用户的相同密码。这是一个给定的,即使是用户名。
- @阿维德:没错。但如果我知道密码的散列值,并且知道salt是我的用户名,那么我就可以轻松地为任何字典单词创建散列的密码值,并将其与其他人的密码散列值进行比较。这就是为什么时间比用户名更好。
- (未经培训)-传统上,试图改善已取得的成果是容易出错的-至少这样可能会分散其他问题的注意力。从我看来,唯一的问题是确保salt的长度足够让事情顺利进行——那么从那以后它的算法强度就差不多了,不是吗?
- 另请参见:stackoverflow.com/questions/1645161/…
- 刚刚在php安全联盟网站上发现一篇文章,在它的示例中使用MD5哈希随机数作为salt phpsec.org/articles/2005/password-hashing.html
salt传统上存储为哈希密码的前缀。这使得任何访问密码散列的攻击者都知道它。将用户名用作salt或不影响该知识,因此它不会影响单个系统的安全性。
但是,将用户名或任何其他用户控制的值用作salt会降低跨系统安全性,因为在使用相同密码哈希算法的多个系统上具有相同用户名和密码的用户最终会在每个系统上使用相同的密码哈希。我不认为这是一个重大的责任,因为作为一个攻击者,我会先尝试一个目标帐户在其他系统上使用的密码,然后再尝试任何其他方法来危害该帐户。相同的哈希只能提前告诉我已知的密码可以工作,它们不会使实际的攻击变得更容易。(不过,请注意,对帐户数据库进行快速比较将提供更高优先级目标的列表,因为它将告诉我谁是谁,谁不重用密码。)
这种想法带来的更大的危险是,用户名通常会被重复使用——例如,几乎任何你想访问的站点都会有一个名为"dave"的用户帐户,"admin"或"root"甚至更为常见——这将使构建针对具有这些常用名称的用户的彩虹表变得更加容易和有效。
这两个缺陷都可以通过在散列密码之前向密码中添加第二个salt值(固定的和隐藏的或像标准salt一样暴露的)来有效地解决,但是在这一点上,您也可以使用标准的entropic salt,而不是在其中使用用户名。
编辑补充:很多人都在谈论熵和盐的熵是否重要。的确如此,但这并不是因为大多数评论都在思考的原因。
一般认为熵很重要,所以攻击者很难猜测盐的存在。这是不正确的,事实上,完全不相关。正如许多人所指出的那样,只有拥有密码数据库的人才能进行受salt影响的攻击,拥有密码数据库的人才能查看每个帐户的salt是什么。你什么时候可以细细地查一下,这是否是可以猜测的无关紧要。
熵之所以重要,是为了避免盐值的聚集。如果salt是基于用户名的,并且您知道大多数系统都有一个名为"root"或"admin"的帐户,那么您可以为这两种salt创建一个彩虹表,它将破坏大多数系统。另一方面,如果使用随机16位盐,并且随机值的分布大致均匀,则需要一个彩虹表来显示所有2^16可能的盐。
这不是为了阻止攻击者知道单个帐户的salt是什么,而是为了不给他们一个单独salt的大而胖的目标,这个目标将被用于潜在目标的很大一部分。
- 同意。我更强调用户名的重用,因为我认为这是对使用用户名作为salt的假设系统最有可能成功的攻击,而对于使用随机salt的系统来说,这是失败的。
- 我感谢你在跨系统安全方面的观点。另外,我想在未来它不太可能看到用户特定的彩虹表…
- @阿维德:你这么认为?这是一个非常特殊的攻击向量。
- 不,例如,如果你想让泄露的帐户发送垃圾邮件,你不介意帐户名是什么。
- @GS:我的意思是使用用户名作为salt是一个非常具体的向量。
- @波塔托海德先生,它不是一个载体,这是我要问的问题——它足够好吗?跨系统安全是一个需要考虑的问题。
- 在这种情况下,是否值得投资于加密安全随机数生成器的额外时间?任何RNG都将防止用户登录冲突。
- 不用于生成salt,不。受salt影响的唯一攻击是直接针对哈希密码进行的攻击。除非他们有你的密码数据库的副本,否则一次攻击就不会造成这种攻击,因为你的密码数据库也会包含盐类。由于攻击者已经拥有了这些salt,因此他们只需要足够的熵就可以避免将多个密码与任何基本(P)RNG提供的相同salt进行散列。
- 第三句话应该是"攻击者做不到…"。
- 所以salt前缀是安全的吗?例如,如果我将密码存储为passhash = md5("urhu2389udfcbdvalk" + UserPassword),这是否安全?
- @Acidzombie24:对所有密码使用一个固定的salt(在我的经验中通常称为"nonce")比对每个密码使用唯一的随机salt安全性差,因为它仍然允许攻击者琐碎地确定两个或多个帐户是否共享同一个密码。另一方面,nonce可能存储在代码中而不是数据库中,因此只有数据库的攻击者将无法访问它;因此,最安全的选项是同时使用系统范围的nonce(存储在代码中)和每个密码的salt(存储在数据库中)。
- +最后一段为1。这让我明白,每个用户都需要盐,而不是一次性使用盐。
- DaveSherohman非常有见识,基于这个建议,我找到了一个非常好的Java库,它可以按照你的建议:Jasyt.Org/加密-PaseWords.HTML。
- 使用用户名还会与密码产生不必要的耦合,因此您无法在不要求密码的情况下创建"更改用户名"功能。
使用高熵盐绝对是安全存储密码的必要条件。
把我的用户名"gs"加在我的密码"my password"上,得到gsmypassword。使用彩虹表很容易打破这一点,因为如果用户名没有足够的熵,那么这个值可能已经存储在彩虹表中,特别是如果用户名很短。
另一个问题是您知道用户参与两个或更多服务的攻击。有很多常见的用户名,可能最重要的是admin和root。如果有人创建了一个彩虹表,其中含有最常用用户名的盐,他可以使用它们来破坏帐户。
他们以前有12块盐。12位是4096种不同的组合。这还不够安全,因为现在可以轻松地存储大量信息。这同样适用于4096个最常用的用户名。您的一些用户可能会选择属于最常见用户名的用户名。
我找到了这个密码检查程序,它计算出你密码的熵。密码中的熵更小(比如使用用户名),使得彩虹表更容易使用,因为它们试图用低熵覆盖至少所有的密码,因为它们更容易发生。
- +1不过,好的一点是,您所说的可能是一个巨大的彩虹桌。要覆盖GSMypassword长度的所有值(假设为混合大小写字母数字),表中需要36^12行!
- 作为后续:分布式彩虹表项目有63970个破解哈希,但36^12是47381338321616896!
- 谢谢,这确实有道理——但正如Potatohead先生指出的那样,您的空间仍然在扩展,超出了使用rt破解的合理可能性。而且,假设我的用户名至少有6-8个字符——熵提供了什么额外的必要值?
- @土豆:假设一个彩虹表包含所有可能的字母数字字符组合。不需要这样做,有效的彩虹表将包含常用密码/哈希的哈希。盐可以防止字典攻击或组合的彩虹表/字典。
- @potate head先生:我的电脑每秒可以产生1000个哈希,63970个哈希没有说明分布式彩虹表存储了多少哈希。
- @GS:因为我可以想象这个项目有超过一分钟的CPU时间,所以我想我的数字是错误的。P
- @potate head先生:上面说的是"完成了10.181亿条链",可能有多少条?
- @GS:不确定——我看到了一些关于链的内容,但更有趣的是数据库——它们最多有8个混合字母数字字符!
- 对于系统来说,将用户名用作salt也是一个坏主意,因为在Windows上有一个可预见的命名为高权限帐户:"administrator"、"root"on*nix、"sa"in mssql等。
- 但是,在大多数应用程序/业务系统中,没有内置的"管理员"、"根"等。
- "高熵盐"并不是一个更大的搜索空间,因为"盐部分"一直是已知的。假设您有4096位salt和8位密码。只要你知道salt,你的搜索空间就只有256个。
的确,用户名本身可能有问题,因为人们可以在不同的网站上共享用户名。但是,如果用户在每个网站上有不同的名称,这应该是相当没有问题的。所以为什么不在每个网站上让它独一无二呢?像这样散列密码
hashfunction("www.yourpage.com/"+用户名+"/"+密码)
这样可以解决问题。我不是密码分析大师,但我肯定怀疑我们不使用高熵这一事实会使散列变得更弱。
- 我相信你是对的,这足够了。然而,考虑到散列是一个复杂的数学领域,而且很有可能低熵盐会使散列在未来更容易猜测(尤其是当散列被破坏时),我不会在安全性上下赌注,因为经过验证的更安全的解决方案并不昂贵。
我喜欢同时使用这两种方法:高熵随机每记录盐,加上记录本身的唯一ID。
虽然这并不能增加字典攻击等的安全性,但它确实消除了一些边缘情况,即有人将其salt和hash复制到另一个记录,目的是用自己的密码替换密码。
(诚然,很难想到这样的情况,但在安全问题上,我看不到安全带和背带有任何危害。)
- 我喜欢将密码绑定到用户。但如果攻击者可以更改密码,他当然也可以更改ID。
- 取决于ID是什么:我使用表的主键,您不能随意更改它。tbh,如果黑客在给你的数据库写信,你已经遇到了很大的麻烦…
- 不,我喜欢。攻击者通常是"内部人"。我可以想象他所追求的不在数据库中的场景,但是如果他可以覆盖数据库中的凭证,他就可以通过身份验证来做他想做的事情。
- 好点。我们确实发现,将第一个用户添加到新的数据库中变得越来越困难:我们有效地使我们的应用程序变得如此安全,我们自己几乎无法对其进行黑客攻击。[另外,使用特定于应用程序的salt,这样您就不能将凭证从一个数据库复制到另一个数据库。]
- 最后,我发现有人这样说:添加一些用户特定于salt的内容。这样可以避免入侵者将一些凭证复制到另一个用户,并且还可以防止黑客猜测任何密码,如果他成功地访问了表中的哈希和salt字段。我更喜欢做的一件事是:不要在散列中使用整数次迭代。不要去10公里或20公里,而是像19835年。
我会说,只要每个密码的盐是不同的,你可能会没事的。盐的要点是,不能使用标准的彩虹表来解决数据库中的每个密码。因此,如果对每个密码应用不同的salt(即使它不是随机的),攻击者基本上必须为每个密码计算一个新的彩虹表,因为每个密码使用不同的salt。
使用具有更多熵的salt并没有什么帮助,因为在本例中,假定攻击者已经拥有数据库。因为您需要能够重新创建哈希,所以您必须已经知道盐是什么。所以您必须将盐或组成盐的值存储在文件中。在Linux这样的系统中,获取salt的方法是已知的,因此没有使用秘密salt。您必须假设拥有散列值的攻击者也可能知道您的salt值。
- "盐的要点是,你不能用标准的彩虹表来解决数据库中的每一个密码"。我不同意。关键是你不能用标准的彩虹表来解密码。如果salt是用户名,"root"的彩虹表可能会破解root的pw。
- 这可能是唯一真正重要的规则。基本上,你希望密码在你的系统中是独一无二的,但不管它是什么。不过,这可能还是很简单的。你不需要大量的熵。
- 这也是我的想法-但我被困在"可能还可以"。我仍然看不到这个理论被证明——或者至少是被证明没有密码分析的含义。
- @OneByone:如果salt由用户名+常量本地值(我经常使用":")组成,那么它仍然会破坏标准化的RTS,除非其中有各种通用用户名和通用静态salt值。我很怀疑事实并非如此。
- 和NSAyer一起。您可以使用一个系统范围的常量字符串,这样就不会有一个预生成的rt,比如(D83D8,以及用户名作为salt),这很可能会防止任何彩虹表攻击。
如果盐是已知的或很容易猜测的,则不会增加字典攻击的难度。甚至可以创建一个考虑到"恒定"盐的改进彩虹表。
使用独特的盐会增加大量字典攻击的难度。
拥有独特的密码强盐值是理想的。
- 彩虹表的全部要点是,值是预先生成的,如果您正在为一个值(例如密码)加上一个给定的常量生成表,这是一个完全不同的表,那么您也可以直接尝试散列结果。:)
- 一个常量散列值不了什么。所有密码都可以使用一个彩虹表破解。盐的作用是不可能创造出彩虹桌。
- @我不明白为什么你不能建立一个彩虹表,它"包括"恒定盐的影响。攻击空间(密码)不会改变,所以表不需要增长,只需要重新计算。
- @土豆-取决于权衡。如果您公司的20K登录都是用相同的常量salt进行哈希运算,那么计算一个新的彩虹表可能比字典分别攻击它们要便宜。因此我强调"批量"。
哈希函数的强度不是由其输入决定的!
使用攻击者已知的salt显然会使构建彩虹表(尤其是对于像root这样的硬编码用户名)更具吸引力,但不会削弱哈希。使用攻击者不知道的salt会使系统更难攻击。
用户名和密码的串联可能仍然为智能彩虹表提供一个条目,因此使用一系列伪随机字符的salt(与哈希密码一起存储)可能是一个更好的主意。举例来说,如果我有用户名"potato"和密码"beer",那么哈希的连接输入是"potatobeer",这是彩虹表的合理输入。
每次用户更改密码时更改salt可能有助于抵御长时间的攻击,同样,执行合理的密码策略也会有所帮助,例如大小写混合、标点符号、最小长度、n周后更改。
但是,我认为您选择的摘要算法更重要。例如,对于生成彩虹表的人来说,使用sha-512比使用md5更痛苦。
- 函数的强度不会改变,但输出肯定会改变。如果输入可以被影响或知道,那么也许可以对散列值进行推断。这就是风险所在。
- @马丁:如果你是否匹配的话,你唯一能从散列值中推断出的东西就是!将"roota"或"rootb"(其中"a"和"b"表示密码)放入散列函数中会得到完全不同的输出。
- 使用彩虹表的攻击者知道这些盐。
- @格斯:你能在这方面做进一步的扩展吗?
- 服务器必须知道未加密的salt(以便对照哈希检查密码),因此可以认为攻击者可以访问salt,或者可以很容易地获取它。
- @谢谢你的澄清。
- 对,对,这是一个给定的-更具体地说,我指的是整个密码系统(或-子系统,WRT散列)的强度。
- @Avid:由于用户名+密码连接可能仍然会给您一个智能彩虹表的条目,我想说最好使用随机字符序列作为您的salt,并将salt存储在相同的位置。如果您愿意,可以在用户每次登录时更改salt。
- 土豆啤酒!我爱你!
salt应该具有尽可能多的熵,以确保如果给定的输入值被多次散列,那么得到的散列值将尽可能接近,总是不同的。
在salt中使用具有尽可能多的熵的不断变化的salt值将确保散列的可能性(例如,密码+salt)将产生完全不同的散列值。
盐中的熵越小,生成相同盐值的机会就越大,因此生成相同哈希值的机会就越大。
当输入已知时,散列值是"常量"的性质,"常量"允许字典攻击或彩虹表如此有效。通过尽可能改变产生的散列值(使用高熵的盐值),确保散列相同的输入+随机盐将产生许多不同的散列值结果,从而击败(或至少大大降低)彩虹表攻击的效果。
- 同样,我并不是指使用一个常量salt——而是一个非随机的,但用户唯一的值。例如用户标识。
- 关键是你的盐值永远不应该是常数。您散列的每一件事情,无论相同的"输入"值与否,都应该有不同的salt。DaveSherohman解释了使用用户唯一值的缺点,这些值在每个哈希计算中基本保持不变。
熵是盐值的点。
如果盐后面有一些简单和可复制的"数学",那就和盐不存在时一样。只需增加时间值就可以了。
- 我不明白这个评论。你说"使用熵",然后说"使用时间"??
- 如果用户名用于salt,则说明它的熵不够。但是,如果您使用用户名+当前日期时间-是的,因为很难猜测盐是什么时候创建的。
- "足够的熵"是主观的,这是你的标准。基于时间的这个值可能不够。
- 没有"绝对真实的随机性"这样的东西。我们应该说什么时候"足够好"。如果你认为在这种情况下时间不够好,那就用别的方法。stackoverflow.com/questions/84556/…
- 真正的随机性:random.org
- 实际上,salt的目的是使每个哈希结果都是唯一的,以防止(a)彩虹表攻击和(b)相同的密码标识。问题是,熵还需要什么,如果有的话?
- 设想一个在每个月的第一天创建成批帐户(随机密码)的系统。想象一下,它使用低粒度的盐时间(或者是一个真正快速的系统,每个时间单位创建数百个帐户)。现在我们很容易受到阿维德(a)和(b)的攻击。
- @阿维:如果我们把你的问题转过来:什么时候用高熵盐不好?额外的CPU周期?可预测的prng?
- @马丁,问题其实不是高熵的问题,而是任何附加信息的"复杂性",因为我们已经有了一个独特的用户名,很难让他们相信这是必要的……