最佳实践:盐渍和胡椒密码?

Gli*_*ire 141 security passwords hash salt password-hash

我遇到了一个讨论,其中我了解到我一直在做的事情实际上并没有腌制密码而是给它们加油,而且我已经开始使用以下功能:

hash_function($salt.hash_function($pepper.$password)) [multiple iterations]
Run Code Online (Sandbox Code Playgroud)

忽略所选的哈希算法(我希望这是对盐和辣椒的讨论,而不是特定的算法,但我使用的是安全算法),这是一个安全的选择还是我应该做一些不同的事情?对于那些不熟悉这些条款的人:

  • 是通常存储与设计,使其不可能使用哈希表来破解密码的数据库中的字符串中的随机生成值.由于每个密码都有自己的盐,因此必须对它们进行单独的强制攻击才能破解它们; 但是,由于salt使用密码哈希存储在数据库中,因此数据库泄露意味着丢失两者.

  • 胡椒是从中旨在是秘密的数据库(在应用程序的源代码通常硬编码)分开存储站点范围静态值.它的使用是为了破坏数据库不会导致整个应用程序的密码表变得暴力破解.

有什么我缺少的,并且正在腌制和密封我的密码是保护用户安全的最佳选择吗?这样做有没有潜在的安全漏洞?

注意:为了讨论的目的,假设应用程序和数据库存储在不同的机器上,不共享密码等,因此违反数据库服务器并不会自动意味着破坏应用程序服务器.

irc*_*ell 298

好.看到我需要写这个,并,我会独自做最后一次典型的答案对辣椒.

辣椒的明显好处

很明显辣椒应该使哈希函数更安全.我的意思是,如果攻击者只获取您的数据库,那么您的用户密码应该是安全的,对吧?似乎合乎逻辑,对吧?

这就是为什么很多人都认为辣椒是个好主意的原因.这说得通".

辣椒的现实

在安全和加密领域,"有意义"是不够的.有些事情是可证明意义,以便它被认为是安全的.此外,它必须以可维护的方式实现.无法维护的最安全的系统被认为是不安全的(因为如果该安全性的任何部分发生故障,整个系统就会崩溃).

胡椒既不适合可证明的模型,也不适合可维护的模型......

辣椒的理论问题

现在我们已经开始了舞台,让我们来看看辣椒有什么问题.

  • 将一个哈希加入另一个哈希可能很危险.

    在你的例子中,你做到了hash_function($salt . hash_function($pepper . $password)).

    我们从过去的经验中知道,"将一个哈希结果"加入另一个哈希函数会降低整体安全性.原因是两个哈希函数都可以成为攻击目标.

    这就是为什么像PBKDF2这样的算法使用特殊操作来组合它们(在这种情况下是hmac).

    关键在于虽然这不是什么大不了的事情,但抛出它并不是一件轻而易举的事.加密系统旨在避免"应该工​​作"的情况,而是专注于"设计工作"的情况.

    虽然这似乎纯粹是理论上的,但实际上并非如此.例如,Bcrypt不能接受任意密码.所以路过bcrypt(hash(pw), salt)的确可以导致比远远弱于哈希bcrypt(pw, salt)如果hash()返回的二进制字符串.

  • 反对设计

    设计bcrypt(和其他密码哈希算法)的方法是使用salt.胡椒的概念从未被引入.这可能看似微不足道,但事实并非如此.原因是盐不是秘密.它只是攻击者可以知道的值.另一方面,辣椒的定义是密码秘密.

    当前的密码哈希算法(bcrypt,pbkdf2等)都设计为只接受一个秘密值(密码).在算法中添加另一个秘密还没有被研究过.

    这并不意味着它不安全.这意味着我们不知道它是否安全.安全性和密码学的一般建议是,如果我们不知道,那就不是.

    因此,在密码学家设计和审查算法以使用秘密值(辣椒)之前,不应将当前算法与它们一起使用.

  • 复杂性是安全的敌人

    信不信由你,复杂性是安全的敌人.制作看起来复杂的算法可能是安全的,也可能不是.但是,它的安全性非常大.

辣椒的重大问题

  • 它不可维护

    您对辣椒的实施排除了旋转辣椒键的能力.由于胡椒用于单向函数的输入,因此您永远不能在值的生命周期内更改胡椒.这意味着你需要想出一些不稳定的黑客来让它支持关键轮换.

    非常重要,因为每当您存储加密机密时都需要它.没有机制来轮换密钥(定期和违规后)是一个巨大的安全漏洞.

    而你目前的胡椒方法要求每个用户通过轮换完全无效的密码,或等到他们下次登录轮换(可能永远不会)...

    这基本上使你的方法立即禁止.

  • 它需要你滚动你自己的加密

    由于当前没有算法支持胡椒的概念,因此它需要您编写算法或发明新算法以支持胡椒.如果你不能立即明白为什么这是一件非常糟糕的事情:

    任何人,从最无能的业余爱好者到最好的密码学家,都可以创建一个他自己无法破解的算法.

    永远不要滚动自己的加密......

更好的方式

因此,在上面详述的所有问题中,有两种方法可以处理这种情况.

  • 只需使用它们存在的算法

    如果您正确使用bcrypt或scrypt(成本很高),除了最弱的字典密码之外的所有密码应该在统计上是安全的.成本为5的哈希bcrypt的当前记录是每秒71k哈希.按照这个速度,即使是6个字符的随机密码也需要数年才能破解.考虑到我的最低建议成本是10,这会使每秒的哈希值减少32倍.所以我们每秒只能说约2200个哈希值.按照这个速度,即使是一些字典短语或修改也可能是安全的.

    此外,我们应该在门口检查那些弱类密码而不允许它们进入.随着密码破解变得更加先进,密码质量要求也应该提高.它仍然是一个统计游戏,但通过适当的存储技术和强密码,每个人都应该非常安全......

  • 在存储之前加密输出哈希值

    在安全领域中存在一种算法,用于处理我们上面提到的所有内容.这是一个分组密码.它很好,因为它是可逆的,所以我们可以旋转键(耶!可维护性!).它很好,因为它被用于设计.这很好,因为它没有给用户任何信息.

    让我们再看看那一行.假设攻击者知道你的算法(这是安全所必需的,否则它是通过默默无闻的安全性).使用传统的胡椒方法,攻击者可以创建一个哨兵密码,因为他知道盐和输出,他可以强制胡椒.好吧,这是一个很长的镜头,但它是可能的.使用密码,攻击者什么也得不到.由于盐是随机的,因此哨兵密码甚至无法帮助他/她.所以他们留下的最好的就是攻击加密的表格.这意味着他们首先必须攻击您的加密哈希以恢复加密密钥,然后攻击哈希.但是有很多关于密码攻击的研究,所以我们要依赖它.

TL/DR

不要使用辣椒.它们存在许多问题,有两种更好的方法:不使用任何服务器端密码(是的,没关系)并在存储之前使用块密码加密输出哈希.

  • @ircmaxell - 另一方面,你什么都不会失去.在最坏的情况下,当密钥变为已知时,攻击者仍必须破解BCrypt哈希(与没有加密的情况相同).这与存储用于加密数据的密钥不同,这与添加服务器端密钥有关.只要攻击者无法控制服务器/代码,即使是硬编码密钥也可以保护这些弱密码.这种情况并不罕见:除了SQL注入,还丢弃备份,丢弃的服务器......可能导致这种情况.许多PHP用户都在托管服务器上工作. (7认同)
  • 这个答案已经非常过时了,它提出的许多主张都不再正确。现代加密函数被设计为接受胡椒作为参数,包括当前一流的 argon2id。/sf/answers/4425582241/ (7认同)
  • 感谢您加入哈希值的最后一部分,这是我完全同意的答案.如果加密将成为[密码api]的一部分(http://www.php.net/manual/de/function.password-hash.php),则没有理由不使用它,所以也许.. (我很乐意为它编写文档) (6认同)
  • @martinstoeckli:我不同意将加密步骤添加到简化的散列API中.原因是秘密(钥匙)的存储比人们意识到的要困难得多,并且很容易在脚下拍摄自己.对于99.9%的用户来说,原始的bcrypt对于除了最简单的密码以外的所有用户来说已经绰绰有余了...... (2认同)
  • @martinstoeckli我不得不同意ircmaxwell这里,我不认为加密属于密码api.虽然,可以注意到,为了最大限度地提高安全性,加密哈希是一个可以使用的附加规定. (2认同)
  • 有人指出 [OWASP 应用程序安全验证标准 4.0](https://www.owasp.org/images/d/d4/OWASP_Application_Security_Verification_Standard_4.0-en.pdf) 第 2.4.5 节建议使用秘密盐(又名胡椒) ):“使用只有验证者知道的秘密盐值来验证是否执行了密钥派生函数的额外迭代。” (2认同)

mar*_*kli 23

我们应该谈谈辣椒确切优势:

  • 在特殊情况下,胡椒可以保护弱密码免受字典攻击,在这种情况下,攻击者可以读取数据库(包含哈希值),但无法使用胡椒访问源代码.

典型的情况是SQL注入,丢弃备份,丢弃的服务器......这些情况并不像听起来那么罕见,而且通常不在您的控制之下(服务器托管).如果你用...

  • 每个密码一个独特的盐
  • 像BCrypt这样的慢速哈希算法

...强密码得到很好的保护.在这些条件下,即使已知盐,也几乎不可能强制使用强密码.问题是弱密码,它们是蛮力字典的一部分或者是它们的派生.字典攻击会非常快速地显示这些,因为您只测试最常用的密码.

第二个问题是如何应用辣椒

一种经常推荐的应用胡椒的方法是在将密码和胡椒传递给哈希函数之前将其组合:

$pepperedPassword = hash_hmac('sha512', $password, $pepper);
$passwordHash = bcrypt($pepperedPassword);
Run Code Online (Sandbox Code Playgroud)

还有另一种更好的方法:

$passwordHash = bcrypt($password);
$encryptedHash = encrypt($passwordHash, $serverSideKey);
Run Code Online (Sandbox Code Playgroud)

这不仅允许添加服务器端密钥,还允许交换$ serverSideKey,如果这是必要的话.此方法涉及更多工作,但如果代码一旦存在(库),则没有理由不使用它.


ben*_*nrg 5

我希望这是关于盐和胡椒的讨论,而不是特定的算法,但我使用的是安全的算法

我所知道的每个安全密码哈希函数都将密码和盐(以及秘密/胡椒,如果支持)作为单独的参数,并自行完成所有工作。

仅通过您连接字符串并且只接受一个参数这一事实hash_function,我就知道您没有使用那些经过充分测试、充分分析的标准算法之一,而是尝试推出自己的算法。不要那样做。

Argon2赢得了 2015 年密码哈希竞赛,据我所知它仍然是新设计的最佳选择。它通过 K 参数(称为“秘密值”或“密钥”)支持胡椒。我知道没有理由不使用胡椒。在最坏的情况下,胡椒将与数据库一起受到损害,并且您的情况并不比不使用它更糟。

如果您无法使用内置的胡椒支持,您可以使用本讨论中建议的两个公式之一:

Argon2(salt, HMAC(pepper, password))   or   HMAC(pepper, Argon2(salt, password))
Run Code Online (Sandbox Code Playgroud)

重要提示:如果将 HMAC(或任何其他散列函数)的输出传递给 Argon2(或任何其他密码散列函数),请确保密码散列函数支持嵌入的零字节,或者对散列值进行编码(例如,以 base64 形式) )以确保没有零字节。如果您使用的语言的字符串支持嵌入零字节,那么您可能是安全的,除非该语言是 PHP,但无论如何我都会检查。