我遇到了一个讨论,在讨论中我发现我所做的并不是加盐密码,而是加盐密码,从那时起我就开始使用类似以下的功能:
1
| hash_function($salt.hash_function($pepper.$password)) [multiple iterations] |
忽略所选的哈希算法(我希望这是一个关于盐和胡椒的讨论,而不是特定的算法,但我使用的是一个安全的算法),这是一个安全的选项,还是我应该做些不同的事情?对于不熟悉这些术语的人:
salt是一个随机生成的值,通常与字符串一起存储在数据库中,目的是使不可能使用哈希表破解密码。由于每个密码都有自己的salt,因此必须逐个强制它们,才能破解它们;但是,由于salt与密码哈希一起存储在数据库中,数据库折衷意味着两者都会丢失。
Pepper是一个站点范围的静态值,与数据库分开存储(通常在应用程序的源代码中进行硬编码),这是为了保密。它的使用是为了让数据库的折衷不会导致整个应用程序的密码表是可强制执行的。
是否有我丢失的东西,加盐和灌输我的密码是保护我的用户安全的最佳选择?这样做有什么潜在的安全缺陷吗?
注意:为了讨论的目的,假设应用程序和数据库存储在单独的计算机上,不共享密码等,因此数据库服务器的破坏不会自动意味着应用程序服务器的破坏。
- 不完全重复,但非常相关:stackoverflow.com/questions/16594613/…
- 跨站点复制:security.stackexchange.com/q/3272/2113
好啊。既然我需要一遍又一遍地写这篇文章,我将只做最后一个关于胡椒粉的标准答案。好的。辣椒的明显优点
很明显,胡椒应该使哈希函数更安全。我的意思是,如果攻击者只获取您的数据库,那么您的用户密码应该是安全的,对吗?似乎合乎逻辑,对吧?好的。
这就是为什么这么多人相信辣椒是个好主意。这"有道理"。好的。辣椒的现实
在安全和密码学领域,"合理"是不够的。一些事情必须被证明是有意义的,以便被认为是安全的。此外,它必须以可维护的方式实现。无法维护的最安全的系统被认为是不安全的(因为如果该安全的任何部分崩溃,整个系统就会崩溃)。好的。
辣椒既不符合可证明的模式,也不符合可维护的模式…好的。辣椒的理论问题
既然我们已经准备好了,让我们来看看辣椒有什么问题。好的。
将一个哈希值输入另一个哈希值是危险的。好的。
在您的示例中,您执行hash_function($salt . hash_function($pepper . $password))。好的。
我们从过去的经验中知道,"仅仅将"一个哈希结果输入另一个哈希函数可以降低整体安全性。原因是这两个哈希函数都可以成为攻击目标。好的。
这就是为什么像pbkdf2这样的算法使用特殊的操作来组合它们(在这种情况下是hmac)。好的。
重点是,虽然这不是什么大问题,但仅仅是扔东西也不是一件小事。加密系统的设计是为了避免"应该工作"的情况,而将重点放在"设计工作"的情况上。好的。
虽然这似乎纯粹是理论上的,但事实上并非如此。例如,bcrypt不能接受任意密码。因此,如果hash()返回二进制字符串,那么传递bcrypt(hash(pw), salt)确实会导致比bcrypt(pw, salt)弱得多的哈希。好的。
反设计工作好的。
bcrypt(和其他密码散列算法)的设计方法是使用salt。胡椒的概念从未被引入。这可能看起来很琐碎,但事实并非如此。原因是盐不是秘密。它只是一个攻击者可以知道的值。另一方面,根据定义,胡椒是一个密码秘密。好的。
当前的密码散列算法(bcrypt、pbkdf2等)都设计为只接受一个秘密值(密码)。在算法中加入另一个秘密还没有被研究过。好的。
这并不意味着它不安全。这意味着我们不知道它是否安全。安全和密码学的一般建议是,如果我们不知道,就不知道。好的。
因此,在密码学设计和审查用于秘密值(Peppers)的算法之前,当前的算法不应该与它们一起使用。好的。
复杂性是安全的敌人好的。
信不信由你,复杂性是安全的敌人。使一个算法看起来很复杂可能是安全的,也可能不是。但它不安全的可能性非常大。好的。
辣椒的重大问题
它不可维护好的。
您对Peppers的实现排除了旋转Pepper键的能力。由于Pepper用于单向函数的输入,因此在该值的生命周期内永远不能更改Pepper。这意味着你需要想出一些奇怪的黑客来支持钥匙旋转。好的。
这是非常重要的,因为在您存储加密机密时,它是必需的。没有旋转密钥的机制(周期性地,在破坏之后)是一个巨大的安全漏洞。好的。
而您当前的Pepper方法将要求每个用户要么将其密码完全无效,要么等到下一次登录Rotate为止(这可能永远不会发生)。好的。
这基本上使你的方法立即停止。好的。
它要求你使用自己的密码好的。
由于目前没有任何算法支持胡椒的概念,所以它要求您要么编写算法,要么发明新的算法来支持胡椒。如果你不能马上明白为什么这是一件非常糟糕的事情:好的。
Anyone, from the most clueless amateur to the best cryptographer, can create an algorithm that he himself can't break.
Ok.
不要使用自己的密码…好的。
更好的方法
因此,在上面详细介绍的所有问题中,有两种方法可以处理这种情况。好的。
只要使用现有的算法好的。
如果正确使用bcrypt或scrypt(成本很高),那么除了最弱的字典密码之外,所有密码都应该在统计上是安全的。以5为代价散列bcrypt的当前记录是每秒71k个散列。以这种速度,即使是6个字符的随机密码也需要多年才能破解。考虑到我推荐的最低成本是10,这就将每秒的散列数减少了32倍。所以我们只讨论每秒2200个哈希。按这个速度,即使是一些字典短语或修饰语也可能是安全的。好的。
此外,我们应该在门口检查那些弱密码类,并且不允许它们进入。随着密码破解变得越来越高级,密码质量要求也应如此。这仍然是一个统计游戏,但有了适当的存储技术和强大的密码,每个人实际上都应该非常安全…好的。
存储前加密输出哈希好的。
在安全领域中存在一种算法,可以处理我们上面所说的一切。这是分组密码。很好,因为它是可逆的,所以我们可以旋转键(耶!可维护性!)很好,因为它是按设计使用的。它很好,因为它不给用户任何信息。好的。
让我们再看一次那条线。假设攻击者知道您的算法(这是安全性所必需的,否则它是通过模糊来实现安全性的)。使用传统的Pepper方法,攻击者可以创建一个哨兵密码,因为他知道盐和输出,所以他可以对Pepper进行暴力破解。好吧,这是一个很长的机会,但这是可能的。有了密码,攻击者什么也得不到。因为salt是随机的,所以一个哨兵密码甚至不能帮助他/她。所以最好的办法就是攻击加密的表单。这意味着他们首先必须攻击加密哈希以恢复加密密钥,然后攻击哈希。但是对于密码的攻击有很多研究,所以我们要依赖它。好的。
TL/DR
不要用辣椒。它们有很多问题,有两种更好的方法:不使用任何服务器端机密(是的,没关系)和在存储之前使用块密码加密输出哈希。好的。好啊。
- 感谢包括加密哈希值的最后一部分,这是一个我完全同意的答案。如果加密将成为密码API的一部分,就没有理由不使用它,所以可能…(我想为它写文档)
- @Martinstoeckli:我不同意将加密步骤添加到简化的哈希API中。原因是存储秘密(钥匙)比人们意识到的要困难得多,而且很容易射到自己的脚上。对于99.9%的用户来说,除了最简单的密码外,原始bcrypt已经足够了…
- @在另一边,你什么都不会松掉。在最坏的情况下,当密钥已知时,攻击者仍然必须破解bcrypt散列(与不加密的情况相同)。这与存储用于加密数据的密钥不同,这是关于添加服务器端机密。只要攻击者无法控制服务器/代码,即使是硬编码密钥也可以保护这些弱密码。这种情况并不少见:除了SQL注入,还有丢弃的备份、丢弃的服务器…都可能导致这种情况。很多PHP用户在托管服务器上工作。
- @Martinstoeckli我必须同意ircmaxwell的观点,我认为加密不属于密码API。尽管如此,可以注意到,为了最大程度地安全加密散列值,可以使用附加的规定。
- 我有一个关于存储前输出加密的问题。如果我没有弄错,加密密钥通常用于唯一地或短暂地加密消息。但是,如果您有200000个用户密码,所有密码都用同一个密钥加密并存储在一起,这是否会使攻击密钥变得更容易?现在您有200000条消息,而只有1或2条。
- 不知道"修改"是不是有意的…
- "我们从过去的经验中知道,"仅仅将一个哈希结果输入另一个哈希函数就可以降低整体安全性。"—告诉我一个与Suhosin描述的情况远程相关的真实世界攻击。当然,像sha256(md5(m))这样的操作基本上可以消除其抗冲突性,但是对于密码散列来说,即使这样也不是问题。关于"针对设计工作":虽然参考bcrypt和scrypt可能比较好,但因为这正是人们可能使用的,所以您忽略了最初的问题。
- suhosin的hash_function只有一个输入,suhosin将其称为安全散列函数,这意味着类似于sha256。对于安全哈希函数,单向性是一个明确的设计目标。你不会得到正式的减缩证明,但是认为$password |-> hash_function($salt.hash_function($pepper.$password))比password |-> hash_function($salt.$password)更不单向是荒谬的。而且,提到权限,密码工程建议使用sha256(sha256(m))而不是单个应用程序来对抗长度扩展属性。
- 不过,我非常喜欢您的论点,即加密对工作更有利,因为它允许更容易的管理,而且是一种简单地为保持数据机密而设计的工具。另外,对于密码散列的情况,您必须非常努力地错误地应用加密原语。即使是欧洲央行模式也可以。关于复杂性的论点:我仍然认为,在php 5.5password_hash中静态预加pepper的密码比使用类似mcrypt的库之后加密密码要简单得多。
- @Perseids我在参考中编辑了一个非常真实和实际的攻击,它是在hash()返回原始字节时,通过组合hash函数(特别是bcrypt(hash())。我绝对不是说你永远不能安全地组合哈希函数。我是说,没有研究,你不应该随便做。至于在密码前加一个胡椒粉,这可能会导致问题(既有空字节,也减少了可用的密码空间,因为bcrypt限制为72个字符的密码)。为什么不只是加密并用它来完成,而不是滚动更多的自定义方案呢?
- 噢,我真傻,我知道在下一个要点中,为了本文的目的,您把bcrypt称为散列函数,但没有意识到它在这里的用途。不幸的是,我们称bcrypt为"密码散列函数",而它与加密散列函数几乎没有相似之处(没有任意输入值,没有任意输入长度)。同样(就像在"与设计对抗"中一样),最好保持段落原样,因为许多读者很可能会将bcrypt与安全散列函数混淆。通过添加的链接,其他人知道你在说什么。
- 为什么不只是使用加密:因为有了加密,人们就有可能推出自己的自定义方案(不是发明自己的密码,而是将他们一无所知的组件插在一起)。虽然我们认为加密应该和密码散列一样简单,但可怕的现实是,大多数库都是可怕的。我已经提到了PHP的McLIPT,但即使Java文档也是完全危险的:Ciphi.GeStudio("DES/CBC/PKCS5PADION");
- 现在,正如我上面所说,加密哈希密码是特别宽容的。但如果不是针对bcrypts 72字节的限制,我很乐意建议在密码前加上pepper,这是最不复杂的(因此更安全)理论上合理的解决方案。是的,我知道你可能不同意防止密钥翻转值得减少复杂性。首先,我更担心加密代码可能出错的所有方式,而不是被泄露的胡椒的威胁。
- 我知道这个答案很古老,但似乎"辣椒"有多种含义。如果胡椒是一个随机选择的字节(0x00…0xFF),并且根本没有存储,该怎么办?验证密码:随机尝试每个字节作为胡椒。成功时平均128次迭代,失败时总是256次迭代。这意味着要多次调用散列函数(argon2或类似函数),使强制密码的速度慢得多,但有效密码可以接受。
- @那将是一个不同的概念。不说它不好,也不说它没用。但是"Pepper"被明确定义为一个不与哈希一起存储的应用程序秘密。所以你提出的是一个新的概念(因此需要一个新的名字)。
- 在过去的很多地方,比如维基百科,我都把它看作是胡椒粉:"胡椒粉很小,每输入一次都是随机生成的,而且从未被存储过。为了验证输入是否匹配,应用程序将迭代胡椒的完整可能值集(以避免定时攻击),依次测试每个结果哈希[1]。
- 1996年,乌迪曼伯写了一篇安静的老论文,描述了这一点:"一个简单的方案,使得基于单向函数的密码更难破解"。在给密码添加新的调味品之前,这很酷。所以作者称之为额外的盐。
- 倾向于同意这里的内容,而不是上面的答案。主要原因是应用层通常不会随数据库一起丢失。除了将散列送入散列之外,我看不到其他参数的支持/原因。
首先,我们应该谈谈胡椒的确切优势:
- 在特殊情况下,Pepper可以保护弱密码免受字典攻击,在这种情况下,攻击者可以读取数据库(包含哈希)的访问权限,但不能使用Pepper访问源代码。
典型的场景是SQL注入、丢弃备份、丢弃服务器…这些情况并不像听起来那么罕见,而且通常不在您的控制之下(服务器托管)。如果你使用…
- 每个密码的唯一盐
- 像bcrypt这样的缓慢散列算法
…强密码受到良好保护。在这种情况下,即使知道salt,也几乎不可能强制强密码。问题是弱密码,这是蛮力字典的一部分,或者是它们的派生。字典攻击会很快发现这些密码,因为您只测试最常见的密码。
第二个问题是如何使用胡椒粉?
应用Pepper的一种常用方法是在将密码和Pepper传递给哈希函数之前将其组合在一起:
1 2
| $pepperedPassword = hash_hmac('sha512', $password, $pepper);
$passwordHash = bcrypt($pepperedPassword); |
但还有另一种更好的方法:
1 2
| $passwordHash = bcrypt($password);
$encryptedHash = encrypt($passwordHash, $serverSideKey); |
这不仅允许添加服务器端机密,还允许在必要时交换$serversidekey。这个方法需要做更多的工作,但是如果代码一旦存在(库),就没有理由不使用它。
- 所以你会说胡椒只比盐更安全,简而言之?感谢您对如何实施的帮助。
- @LightningDust——是的,对于弱密码,只要胡椒粉保密就行。它减轻了一些定义明确的威胁类型。
- @Martinstoeckli绝对是实现这一点的好方法。很高兴看到有一些安全经验的人支持这个方法。mysql AES_ENCRYPT($passwordHash, $serverSideKey)调用是否也是实现这一点的适当方法?
- @Foo_Chow-我不知道MySQL函数的实现,但是他们似乎使用了EBC模式来避免IV向量。与已知的纯文本(哈希值总是以相同的字符开头)一起,这可能是一个问题。在我的主页上,我发布了一个处理这种加密的示例实现。
- @Martinstoeckli很有趣,对这些概念不太熟悉;不过,对于最健壮的结果,似乎需要IV。似乎不会为增加的收益增加太多的开销。
- 你能建议一个人怎样把钥匙换成旧密码吗?遍历所有这些文件并解密和重新加密?
- @mukul-确切地说,您可以用旧密钥解密每个存储的哈希,用新密钥加密并存储在数据库中。密码不在这个过程中,我们只是重新加密密码的散列。
盐和胡椒的要点是增加预先计算的密码查找(称为彩虹表)的成本。
一般来说,试图找到单个哈希的冲突是困难的(假设哈希是安全的)。但是,使用短哈希,可以使用计算机生成所有可能的哈希,以查找硬盘。这叫做彩虹桌。如果您创建了一个彩虹表,那么您就可以进入这个世界,快速找到任何(未加保护的)哈希的可重用密码。
胡椒的要点是使彩虹表需要黑客你的密码列表唯一。因此,在攻击者身上浪费更多的时间来构建彩虹表。
然而,salt的要点是使每个用户的彩虹表对用户来说都是唯一的,这进一步增加了攻击的复杂性。
实际上,计算机安全的重点几乎永远不会使它(在数学上)不可能,只是在数学上和物理上不切实际(例如,在安全系统中,计算单个用户的密码需要宇宙中的所有熵(以及更多熵)。
- 那么,盐+胡椒比盐更安全吗?或者我最好还是扔掉胡椒粉,再重复一遍克里特?
- 彩虹表的主要功能是,您不为特定的攻击创建一个表,而是下载一个预先存在的表。有长长的彩虹表可用于流行的哈希算法,只需一个谷歌就可以了!
- @我自己想不出任何理由。然而,另一条线索里的理查德却想出了一个。您可以将胡椒粉隐藏在源代码中,这意味着攻击者需要访问另一个地方,只需将彩虹表放在一起即可。
- @Aron——我就是这么想的,因为我们有独立于数据库服务器的应用服务器(也就是说,如果您在我们的数据库服务器上使用root,那么您仍然无法访问我们的应用服务器),将pepper隐藏在源代码中(在我们的配置文件中)将提供额外的安全性。
看不到在源代码中存储硬编码值具有任何安全相关性。这是通过默默无闻的安全。
如果黑客获取了你的数据库,他将能够开始暴力强迫你的用户密码。如果那个黑客破解了一些密码,他很快就能识别出你的胡椒粉。
- 它将使一个预计算的彩虹表对于一个没有安全保护的密码表无效。简而言之,即使攻击者知道你的胡椒,他也需要创建一个新的彩虹表。
- 它加强了基于数据库中不可用数据的哈希,这是一件好事。数据库中的内容可能会暴露在漏洞中——以同样的方式访问代码中的值的可能性较小。
- 盐是一种单向功能,即使成功地强行输入密码也只会给你这个密码,而且不能帮助你获得胡椒值本身。
- 这里的md5示例使用md5($pepper.$password)(注意:我知道不在活动站点上使用md5,这是为了举例),我的密码是cat,我的哈希是2d1ec2609e8265e64f2dbf6a697ebbfe。我的胡椒粉是什么?在最好的情况下,你所能做的就是尝试蛮力地迫使我(通常是64个字符)胡椒。
- @我认为你的意思是"散列是陷阱门的功能"。是的,不可能从一个安全哈希(实际上这是安全哈希的定义)中找出盐和/或胡椒。
- 有趣的讨论。但从未建议您从哈希中收集密码。我认为辣椒会产生更多的复杂性,但不会带来更多的安全性。
- @Svenandersrobbestad——你确实说过"黑客识别你的胡椒不会花很长时间",我认为这是事实上不正确的。即使有了100个正确的密码和由此产生的哈希,你也不会知道胡椒是什么。
- @svenadersrobbestad实际上复杂性是安全性。我们在这里讨论的是巨大的复杂性数字。当所有的一切都说了算,用一种好的盐和一种好的胡椒粉,即使你能神奇地把宇宙中的每一个质子转换成一个硬盘,你也无法存储由此产生的彩虹表。
- @事实上,我认为你误解了一个重要的安全建议(通过默默无闻的安全是不好的)。这个建议并不是全部的事实。正确的工作方式是,所有的模糊都应该集中在一把钥匙上,这是一个确保钥匙安全的问题。在这种情况下,胡椒在某种意义上是一把密码钥匙。
- @通过模糊来实现安全是一种有效的防御手段。关键是,它不应该被当作防御手段,而应该被用来"减慢攻击者的速度"。如果不是这样的话,就不会用蜜罐之类的东西了。相反,只要我们不依赖安全性来保证应用程序的安全性,我们就可以通过默默无闻有效地利用安全性来帮助降低攻击者的速度。