今天我们在课堂上讨论了密码哈希和加盐。我们的教授对我的盐的用例有完全不同的理解,并说您可能根本不存储盐,而只是检查每次登录尝试是否包含所有可能的盐并授权是否匹配。

我真的看不到任何意义,因为现在我的服务更容易受到蛮力攻击(发送一个密码,服务器检查许多密码),但是我猜想对于针对散列密码的纯字典攻击更安全。

那么,有没有用例人们会真正做到这一点?

评论

考虑到盐的长度,如果您必须尝试所有可能的盐值,这可能会导致非常冗长的(不切实际的)登录操作。这将导致使用较小的盐范围以提高可用性。我认为与使用有效的盐值并将其存储相比,这可能导致更差的安全性。另请参见stackoverflow.com/questions/184112/…,以获取有关理想盐长度的讨论,建议长度在16到256位之间。

这位教授到底是做什么的?牙科?

请注意,良好的密码哈希在设计上很慢,因此可能的盐值范围很快就会变得太大。

这就是为什么人们应该向Stack Exchange学习而不是向教授学习。 :-)

如果您的数据库在潮湿的地方,AFAIK储存盐是个坏主意-会导致腐蚀(请参见此处的第二人生腐蚀表的示例)。

#1 楼

不储存盐是个坏建议。

盐的主要目的是必须分别攻击每个用户密码。

如果不储存盐,那么,如您所说,您需要尝试每种单一的盐组合才能验证密码。如果需要检查每种盐组合,则意味着盐不能太长(在注释中提到了12位)。如果加盐时间不长,则表示将对许多用户重复加盐。如果对许多用户重复此操作,则意味着攻击者将能够同时攻击许多用户,这将节省攻击者的时间。

这样做,您几乎完全无法使用盐。

评论


难道还不存在更多冲突和更多误报的可能性吗?

–咖啡因成瘾
16年7月15日在14:24

@CaffeineAddiction也许。例如,可能发生以下情况:哈希(错误的盐+正确的密码)=哈希(正确的盐+正确的密码)。但这不是实际的攻击方法,因为在哈希函数中创建冲突非常困难。

–古德拉丹
16年7月15日在14:36

@CaffeineAddiction:不太可能。假设使用SHA-256和32位盐,则您与现有哈希(也称为第二原像)发生冲突的几率从2 ^ -256变为2 ^ -224。在事物的宏伟方案中,更大但仍然是无限的。

–大卫
16年7月15日在15:58

@David怎么样?哈希函数可以建模为任意两个输入均导致独立输出的随机预言机,因此我认为概率根本不会发生变化(除非您将输入填充到固定长度)

–托马斯
16年7月17日在12:40

@Thomas您有一个从 生成的哈希。每次用户输入密码时,服务器都会检查您的密码以及2 ^ 32种可能的密码。例如,如果您使用的是32位哈希,则意味着您的真实密码可以使用,但是如果您输入任何密码,则哈希很有可能与服务器尝试的2 ^ 32之一匹配。

–詹森·古玛(Jason Goemaat)
16年7月19日在7:11

#2 楼

一种“秘密”盐被称为胡椒粉。

来自维基百科:


除了盐值,还可以将胡椒粉添加到密码中。胡椒粉与盐的作用相似,但是盐通常与散列的值一起存储,对于要定义为胡椒粉的东西,它应满足以下条件之一,将其定义为更精心隐藏的“秘密”比盐值低:


胡椒粉与要散列的值分开保存
胡椒粉是针对要散列的每个值随机生成的(在一组有限的值内) ),并且从不存储。当针对匹配的哈希值测试数据时,这是通过迭代对胡椒有效的一组值来完成的,然后依次将每个值添加到要测试的数据中(通常通过将其后缀添加到数据中),在对组合值运行加密哈希函数之前。



胡椒的优点在于,攻击者现在必须猜测出胡椒的可能排列数量

辣椒增加了特定哈希的攻击时间,而盐则没有。

请记住,盐是缓解预先计算的哈希的有效方法它使攻击者花费大量时间来攻击一组哈希。但是,如果仅考虑一个散列,并且不使用任何预先计算的散列,则盐不会增加攻击时间。但是,Pepper迫使攻击者对每个纯文本密码使用多个猜测,即使是单个哈希也是如此。

这样,胡椒粉就类似于按键拉伸。

大多数实现方法都喜欢用键拉伸而不是胡椒。

我个人的观察是,大多数实现方案都喜欢用键拉伸而不是胡椒。我没有相关参考,因此读者可以在评论中提供支持或反对的参考。人们倾向于使用键拉伸,因为它具有已知和预期的性能成本和安全性好处。为了计算哈希的第N轮,必须计算N个哈希。但是,用胡椒粉只能计算出预期的尝试次数。考虑一个1字节的Pepper,攻击者将需要256个猜测才能猜测所有可能的组合,但是预期值为128,并且攻击者可以(平均1/256次)猜测第一次尝试的值。

辣椒和键拉伸可以相互配合。

键拉伸很有效,因为您可以根据想要计算a的时间长度来设置回合数哈希值。假设您要在当前硬件上进行一次检查花费半秒钟,则只需增加回合数直到出现这种情况。

对于胡椒,因为您需要为每个密码猜测多个值,所以胡椒的大小必须与轮数成反比,以保持计算时间恒定。

哈希实现的实用建议

密码/哈希实现的最佳建议是使用众所周知的方法和经过测试的库。您应该将bcrypt或pbkdf2与独特的盐和许多回合一起使用。这些算法倾向于以多种语言和框架实现众所周知的实现。如果您碰巧找到了一个著名的经过测试的库,其中除了盐和键拉伸之外还包含胡椒,那也许值得您花些时间使用,但是额外的好处通常会超过性能成本。

评论


难道还不存在更多冲突和更多误报的可能性吗?

–咖啡因成瘾
16年7月15日在14:26

辣椒仍然存储(不是在普通视图中,而是存储)

– WoJ
16年7月15日在14:42

我一直听到的“胡椒粉”定义是一种盐,它按应用程序存储在代码中,而不是按用户存储在数据库中。

– BlueRaja-Danny Pflughoeft
16 Jul 15 '15:44



值得一提的是,胡椒仍然存储在数据库中,而不必存储。 OP似乎在谈论根本不存储盐的情况,并且每次尝试登录时,应用程序本质上都会强制使用盐...

– Ajedi32
16年7月15日在18:41

@amccormack我不确定确切的消息来源,但是几乎我读过的有关辣椒的每篇文章或帖子似乎都具有这个定义。 security.stackexchange.com/q/3272/29865 stackoverflow.com/q/16891729/1157054 blog.filippo.io/salt-and-pepper除此之外,不存储胡椒粉将毫无用处-基本上就是增加哈希函数工作因子的一种真正的技巧。

– Ajedi32
16年7月15日在18:58

#3 楼

背景:您应该使用慢速密码哈希。 (即bcrypt)“慢”是指计算量大,使用DoS保护*花费100毫秒以上的时间(在您的硬件上)来测试单个密码。如果散列被盗,这将提高通过暴力破解(通过攻击者硬件)查找密码所需的处理能力。

强烈建议按用户使用唯一盐。 (对于bcrypt,它是自动生成的)Salt应该是高度唯一的(即长且随机),但不是秘密。使用唯一的盐意味着攻击者必须为每个用户运行单独的蛮力作业。

如果没有“盐”,则攻击者可以立即使用Rainbow Table并且完全没有蛮力。

如果仅使用“共享盐”,则攻击者可以用一个蛮力的Job破解所有用户的密码。 (不如彩虹桌子快,但比每个单独的蛮力作业要容易得多)


答案:如果要“不储存”盐(“蛮力”


您的教授建议的那样)


可能的盐必须非常少
哈希必须要快得多

这完全会破坏Salt的目的,严重削弱了Slow hash的好处。那是您教授的主要设计错误。基本上,他正在制定自己的密码存储方案,在该方案中,应使用打算使用的经过严格审查的bcrypt算法(或scrypt或PBKDF2)。


*与@纳文评论说,这可能是潜在的DoS攻击媒介。一种解决方案是限制每个IP和每个用户名每小时的尝试次数。也有可能应该将哈希的“慢度”降低到仅10ms。从“被盗的散列”的角度来看,这几乎不及100ms,但仍然比“微秒”更好。

评论


“如果不加盐,攻击者可以在一个蛮力作业中对所有用户进行蛮力哈希(这样可以节省很多时间)。”或者只是从数据库中为每个用户选择带有MD5哈希5f4dcc3b5aa765d61d8327deb882cf99的用户,对应于密码“ password”。这也是一种保护用户免受自身伤害的方法。

– Mikkel
16年7月18日在21:22

切勿使用MD5储存密码!请改用慢散列。但是,是的,我明白你的意思。我已经更新了引用您所引用的Rainbow Tables的答案。

–布莱恩·菲尔德(Bryan Field)
16年7月19日在12:08

真正。在现实世界中,无论使用哪种语言,都应该使用正确的密码哈希库,该库通常在哈希字符串中包含盐,并防止计时攻击,这使整个讨论变得毫无意义。但是,是的,MD5不是密码。

– Mikkel
16年7月19日在13:57

#4 楼

你的教授不正确。加盐的目的是增加散列密码的熵,以防止对其进行任何预计算攻击,并防止来自不同用户的相同密码具有相同的散列值。

能够尝试所有可能的盐值意味着盐中的熵必须非常低,这意味着可以通过彩虹表进行预计算。

#5 楼

您可以通过这种方式使用盐。这将是一种哈希拉伸过程。通常,您通过重复执行数千次算法来扩展哈希,这会使攻击者和用户的速度降低1000倍,但是用户通常不介意这种速度降低。以这种方式使用盐会产生哈希扩展算法的效果,因为必须对许多未知的哈希重复该盐。

但是,这是一种非常不寻常的方法。传统的加盐方法可以使盐达到更好的效果(使盐变得没有人可以预先计算出密码表)。传统的进行哈希扩展的方法做的哈希扩展应该做的要好得多(这样做使攻击者需要更长的时间来计算密码)。以这种方式使用盐有点像将它们两者融合在一起。结果有点像样的工作,但较丑陋的技术混搭效果要好得多,更干净的方法可以同时完成两种解决方案。

#6 楼

我不想以蛮横的方式来思考盐,而是想说它使得无法通过查看密码来告知有关密码的任何信息,包括密码与其他密码的关系。如果系统不加盐,则查看两个用户的哈希密码将表明他们的真实密码是否匹配。如果系统仅使用用户名进行盐腌,而没有使用随机,特定时间或特定于系统的盐,那么在使用相同方法的两台计算机上查看用户的哈希密码将表明两台计算机上的用户密码是否匹配。如果系统使用系统ID和用户名进行盐腌,但没有随机性或特定于时间的情况,则可以由同一用户访问两个不同密码哈希的人可以判断关联的密码是否匹配。

随机加盐的目的是使即使使用相同密码的两个哈希都不会匹配,即使它们涉及相同的用户。如果通过登录尝试强行使用盐可以在不存储盐的情况下达到类似的效果,但是这种方法将限制盐可以使用的实际长度,从而增加了在两种情况下使用密码的可能性。相同的盐,因此可以识别为匹配盐。

#7 楼

盐腌给你什么?攻击者已经预先计算了密码散列值的数据库,包括普通密码和非普通密码。如果他们捕获了您的数据库并为每个用户提供了密码的哈希值,则很容易对照这些值检查哈希值而无需添加盐。

通过随机盐和密码一起存储,这疯狂的快速方法不再可行。但是,如果攻击者既有盐又有哈希,那么仍然可以对弱密码使用字典攻击,或者对短密码使用暴力破解。攻击者所需要做的就是使用盐,然后使用字典或蛮力攻击尝试使用不同的密码。

现在,假设更改密码时,您使用12位随机值对哈希进行哈希处理,不与盐一起存储。然后,每次您检查密码时,都必须尝试所有4096个值。在我的计算机上,这大约需要3.5毫秒,因此每秒可以检查284个密码。有人登录时,服务器上的CPU使用率要高一些,但是对于尝试字典或蛮力攻击的人来说,即使他们有哈希值和盐,您的工作也变得更加困难。

评论


如果他们没有哈希和盐,您是否会使他们的工作容易得多?从现在开始,如果我仅发送一个值,服务器将检查4096个值,从而产生更多的误报。这就是我的教授使用的理由,但对我而言似乎并不是很有用,所以我希望看到有人在实际代码中实际使用此方法。

– jazzpi
16年7月18日在17:28



如果他们没有哈希,那就没有意义了吗?盐腌可以防止有哈希的人。如果使用12位数字变得很流行,攻击者可能会为这些数字中的每个数字以及常见/不常见的密码创建带有预先计算的哈希值的数据库,以使他们的工作更轻松。即使您生成了4096个随机盐,而不是简单地使用它们来代替简单的12位数字,也将使它们只关注一个盐值,如果您拥有数百万个盐,则可能会损害很多用户。

–詹森·古玛(Jason Goemaat)
16年7月19日在2:06

是的,加盐可以防止有哈希的人。通常,它不会使您更容易受到没有哈希的人的侵害,但是此变体确实可以。

– jazzpi
16年7月19日在6:55

因为如果您将一个密码发送到服务器,则它需要使用所有4096种可能的盐检查该密码,从而为您带来更高的误报率。

– jazzpi
16年7月19日在7:28

听起来您正在谈论使用随机密码的哈希算法中的冲突。如果您具有256位哈希,那么在尝试产生冲突时,这4096次尝试几乎毫无意义。拥有哈希值时可以蛮力执行的原因是,您可以每秒在本地计算机上运行数百万或数十亿张支票。如果您的网站允许人们尝试数百万个密码而不将其锁定,则(A)您做错了,(B)他们必须与您的服务器建立快速连接,并且(C)您应注意服务器的CPU正在钉住。

–詹森·古玛(Jason Goemaat)
16年7月19日在16:16

#8 楼

不存储盐的某些受控位数的想法似乎有些好处,它是独立配置的,并且与盐的大小无关。

我们有32位盐。我们可以选择仅存储22位,并在进行身份验证时对其余10位进行蛮力测试。这样做的效果似乎是将更多回合添加到哈希函数中。合法身份认证的影响并不多,但足以增加暴力破解的难度。

明年,计算机将变得更快。因此,我们浏览了密码数据库并从每种盐中剔除了一点:现在我们只存储21位,并且必须通过11进行强行破解。

好像我们将哈希强度加倍了,但是没有替换算法并导致用户重新哈希密码的中断(这取决于常规密码到期策略)。

这种“渐进式盐丢弃”方法可以延长哈希的使用寿命函数。

但是,这些方法会以同等因素减慢合法身份验证和蛮力攻击,因此充其量只能提供较小的安全层。我们的重点应该放在改进上,这些改进只会给合法使用增加恒定的额外时间,同时会增加破解难度。当然,具有此属性的改进是增加了密码短语中的熵!密码中每增加一点熵,都会给合法用户增加一定的成本,但会使破解暴力的工作加倍。长度为N的密码需要O(N)进行哈希(和键入),而O(2 ** N)则需要使用蛮力。在密码中添加12位熵会掩盖12位盐。

评论


“这种“渐进式盐去除”方法可以延长哈希函数的使用寿命。”有一些具有可配置哈希值大小的哈希函数(一个示例是Keccak),在我看来,将它们用作KDF的基础原语似乎是一个更好的主意。您仍然可以使用此技术来延长未更新的密码哈希的使用寿命(通过更新,新的哈希将具有最近配置的哈希大小),但这是否是一个好主意也值得商question。您将在每次登录时看到用户的普通密码(例如SRP除外),因此您可以更新哈希。

–类风湿
16年7月16日在12:14

@Rhymoid您需要对密码数据库条目具有写权限,以在身份验证时(系统短暂知道密码时)更新哈希。例如,在经典的Unix上,每个人都可以读取/ etc / passwd并使用哈希进行身份验证,但是只有setuid实用程序(例如/ bin / passwd)可以更新它们。 (通常,我们可以设想一个系统,在该系统中,更新身份验证数据库条目与将其用于身份验证是一项单独的特权,即使两者都是仅授予某些程序的特殊特权。)

–卡兹
16年7月16日在16:54

#9 楼

在野外,我们有一个用户表。用户表通常是

ID   |  username | salt | encrypted_password               | horridly_insecure_reset_key
===========================================================================
1    | user1     | foo  | 09b6d39aa22fcb8698687e1af09a3af9 | NULL
2    | user2     | bar  | 6c07c60f4b02c644ea1037575eb40005 | NULL
3    | user3     | baz  | 09b6d39aa22fcb8698687e1af09a3af9 | reset


,那么身份验证方法将类似于

def authenticate(user, password)
    u = User.find(user: user)
    return u.encrypted_password == encrypt(password + u.salt)
end


每位用户使用salt,它可以确保即使知道了user1的密码,也无法在没有salt的情况下弄清楚user2或user3的密码。

您还可以通过设置一组加密密码并尝试一些加密密码来确保您不会感到厌烦。

本质上,这样,每次攻击必须从头开始针对用户。

即使攻击者拥有用户和盐的列表,他们仍然需要对每个用户进行破解,以查看他们是否具有密码匹配项。如果您有一堆盐或一个静态盐,我可以知道user1的密码是password,然后只需找到所有匹配的加密密码即可。因此,这种方式至少会使它们的速度降低一点。

现在,当我们研究盐时,我们希望减少盐的重复使用。两种相同的盐将使攻击者更容易使用。如果两个人共享相同的密码和相同的密码,则破坏一个用户将破坏另一个用户。

所以可以说我们只使用这三种盐。我们有3000个用户。也就是说,有1000人食用相同的盐分。如果其中1%的密码为“ password”,那么这些人可以同时全部被破解。一次有10个帐户被黑客入侵。因为我们知道这三种盐。这很容易让30个人立即受到攻击。

现在,如果每种食盐都是独一无二的。而且我们知道user1的密码是password,这对您没有任何好处。您仍然只破解了1个用户。您仍然必须为所有其他2999个用户执行“密码+盐=加密密码”。

一个非常重要的说明。

默默无闻的安全不是安全。这并不意味着您应该在Google上发布用户表,因为这很愚蠢。但是在评估安全性时,您应该假设攻击者拥有一切。您不能说:“但是他们不知道应用程序盐,因为他们没有源代码”。因为他们可以。并不是说放弃您的盐分,而是意味着它不是真正的安全性。假设他们有用户名和盐,然后尝试使他们更难获得密码。

超级重要提示

此处使用的代码和表大约是实际使用的9000倍。密码未加密,盐太短,方法有点简单,简而言之,在生产中做这样的事情并不应该被认为是安全的。我选择这些原因仅仅是为了演示,而不是因为它们是安全的。