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

在一次讨论中,我意识到我所做的并不是给密码加盐,而是给它们加上胡椒粉。从那以后,我就开始使用这两种功能,比如:

hash_function($salt.hash_function($pepper.$password)) [multiple iterations]

忽略所选择的哈希算法(我希望这是一个关于盐和胡椒的讨论,而不是特定的算法,但我使用的是一个安全的算法) ,这是一个安全的选项,还是我应该做一些不同的东西?对于那些不熟悉这些术语的人:

  • 是一个随机生成的值,通常与数据库中的字符串存储在一起,目的是使用哈希表破解密码成为不可能。由于每个密码都有自己的 salt,因此必须对它们分别进行强制破解; 但是,由于 salt 存储在具有密码 hash 的数据库中,因此数据库折衷意味着同时失去这两个元素。

  • Pepper是与数据库分开存储的站点范围内的静态值(通常在应用程序的源代码中硬编码) ,它应该是保密的。它的使用是为了使数据库的妥协不会导致整个应用程序的密码表成为强制的。

我是否遗漏了什么东西,并且在我的密码上撒盐和胡椒粉是保护用户安全的最佳选择?这样做有什么潜在的安全漏洞吗?

注意: 为了讨论的目的,假设应用程序和数据库存储在单独的机器上,不共享密码等,所以数据库服务器的破坏并不自动意味着应用程序服务器的破坏。

56522 次浏览

不能把在源代码中存储硬编码值看作与安全相关,它是隐晦式安全的。

如果一个黑客获取了你的数据库,他将能够开始强迫你的用户密码。如果那个黑客能破解几个密码,他很快就能识别出你的胡椒粉。

椒盐和胡椒的目的是增加预先计算的密码查找(称为彩虹表)的成本。

一般来说,试图找到单个散列的冲突是困难的(假设散列是安全的)。但是,使用短散列,可以使用计算机将所有可能的散列生成到硬盘上的查找中。这个叫做彩虹桌。如果你创建了一个彩虹表,那么你就可以走出去,快速找到任何(无盐无胡椒)散列合理的密码。

胡椒粉的意义在于让黑客密码列表所需的彩虹表变得独一无二。因此在攻击者构建彩虹表上浪费了更多的时间。

然而,咸的意义在于让每个用户的彩虹表对于用户来说都是独一无二的,从而进一步增加了攻击的复杂性。

实际上,计算机安全的重点几乎从来不是让它(在数学上)不可能,只是在数学上和物理上不切实际(例如,在安全系统中,计算一个用户的密码需要宇宙中的所有熵(甚至更多))。

首先我们来谈谈 胡椒的绝对优势:

  • 在特殊情况下,黑客可以读取数据库(包含散列) ,但不能访问使用“胡椒”的源代码,“胡椒”可以保护弱密码不受字典式攻击攻击。

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

  • 每个密码一个惟一的 salt
  • 像 BCrypt 这样的慢散列算法

强大的密码受到很好的保护。在这种情况下,几乎不可能强行设置一个强密码,即使已经知道了 salt。问题在于弱密码,它们是暴力字典的一部分,或者是它们的派生物。字典式攻击会很快揭示这些密码,因为你只测试最常见的密码。

第二个问题是 如何使用胡椒粉

一个经常推荐的应用花椒的方法是,在将密码和花椒传递给 hash 函数之前,先将它们结合起来:

$pepperedPassword = hash_hmac('sha512', $password, $pepper);
$passwordHash = bcrypt($pepperedPassword);

不过,还有一种更好的办法:

$passwordHash = bcrypt($password);
$encryptedHash = encrypt($passwordHash, $serverSideKey);

这不仅允许添加服务器端机密,还允许交换 $serverSideKey,如果需要的话。这个方法需要做更多的工作,但是如果代码已经存在(库) ,就没有理由不使用它。

好吧。鉴于我需要写关于这个 结束了结束了的内容,我将单独对胡椒做最后一个规范的回答。

辣椒的明显优势

似乎很明显,辣椒应该使散列函数更加安全。我的意思是,如果攻击者只得到你的数据库,那么你的用户密码应该是安全的,对不对?听起来很合理,对吧?

这就是为什么那么多人认为辣椒是一个好主意。它“有意义”。

辣椒的现实

在安全和密码学领域,“有意义”是不够的。有些东西必须是可证明的 还有是有意义的,以便它被认为是安全的。此外,它必须以可维护的方式实现。不能维护的最安全的系统被认为是不安全的(因为如果安全性的任何部分崩溃,整个系统就会崩溃)。

辣椒既不符合可证明的模型,也不符合可维护的模型。

辣椒的理论问题

现在我们已经准备好了,让我们来看看辣椒有什么问题。

  • 把一个大麻加到另一个大麻里是很危险的。

    在您的示例中,您执行 hash_function($salt . hash_function($pepper . $password))

    从过去的经验我们知道,“只是提供”一个散列结果到另一个散列函数可以降低整体的安全性。原因是这两个散列函数都可能成为攻击的目标。

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

    关键是,虽然这不是什么大事,但也不是随便乱扔的小事。加密系统的设计是为了避免“应该工作”的情况,而是侧重于“设计工作”的情况。

    虽然这看起来纯粹是理论上的,但实际上并非如此。例如,Bcrypt 不能接受任意密码。因此,如果 hash()返回二进制字符串,那么传递 bcrypt(hash(pw), salt)确实会导致比 bcrypt(pw, salt)弱得多的散列。

  • 反设计工作

    Bcrypt (和其他密码哈希算法)的设计方法是使用 salt。胡椒的概念从未被引入。这可能看起来很琐碎,但其实不是。原因是盐不是秘密。它只是攻击者可以知道的一个值。另一方面,根据定义,胡椒是一个加密秘密。

    当前的密码哈希算法(bcrypt、 pbkdf2等)都被设计为只接受一个秘密值(密码)。在算法中加入另一个秘密根本没有被研究过。

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

    因此,在密码学家设计并审查算法以便与秘密值(辣椒)一起使用之前,目前的算法不应该与它们一起使用。

  • 复杂性是安全的敌人

    信不信由你 复杂性是安全的敌人。制作一个看起来复杂的算法可能是安全的,也可能不是。但它不安全的可能性很大。

辣椒的严重问题

  • 这是不可维护的

    您的辣椒实现排除了旋转辣椒键的能力。因为在单向函数的输入中使用了花椒,所以在值的生命周期内永远不能更改花椒。这意味着您需要想出一些不靠谱的技巧来使其支持键旋转。

    这是 非常的重要性,因为它是存储加密秘密时所必需的。没有一种机制来轮换密钥(定期地,在发生漏洞之后)是一个巨大的安全漏洞。

    并且您目前的辣椒方法将要求每个用户要么让他们的密码完全无效的旋转,或者等到他们的下一次登录旋转(这可能永远不会) ..。

    所以你的方法基本上是不可行的。

  • 需要你自己加密

    由于目前还没有支持胡椒概念的算法,因此需要您编写算法或发明新的算法来支持胡椒。如果你不能马上明白为什么这是一件很糟糕的事情:

    任何人,从最无知的业余爱好者到最好的密码破译者,都可以创造出自己无法破解的算法。

    永远不要卷你自己的密码..。

更好的方式

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

  • 只要使用现有的算法

    如果正确使用 bcrypt 或 crypt (代价高昂) ,除了最弱的字典密码之外,所有密码都应该在统计上是安全的。目前花费5的哈希密码的记录是每秒71k 哈希。按照这个速度,即使是一个6个字符的随机密码也需要数年才能破解。考虑到我的最低建议成本是10,每秒的散列减少了32倍。所以我们讨论的只是每秒2200个哈希。按照这种速度,即使是一些词典短语或修改也可能是安全的。

    此外,我们应该在门口检查那些弱类密码,不允许他们进入。随着密码破解越来越先进,密码质量要求也会越来越高。这仍然是一个统计游戏,但有了适当的存储技术和强大的密码,每个人都应该是非常安全的..。

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

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

    我们再看看那条线。假设攻击者知道您的算法(这是安全性所必需的,否则就是隐晦式安全)。使用传统的胡椒方法,攻击者可以创建一个哨兵密码,由于他知道盐和输出,他可以蛮力胡椒。好吧,希望渺茫,但有可能。有了密码,攻击者什么都得不到。而且因为盐是随机分配的,哨兵密码甚至帮不了他/她。所以最好的办法就是攻击加密表单。这意味着他们首先必须攻击您的加密哈希来恢复加密密钥,然后攻击哈希。但是对于密码攻击有一个 很多的研究,所以我们希望依靠它。

TL/DR

别用辣椒。它们存在很多问题,有两种更好的方法: 不使用任何服务器端机密(是的,没问题)和在存储之前使用分组密码加密输出散列。

我希望这是一个盐和胡椒的讨论,而不是具体的算法,但我使用的是一个安全的

据我所知,每个安全密码哈希函数都使用 password 和 salt (如果支持,还有 secret/pepper)作为单独的参数,并完成所有工作。

仅仅通过串联字符串和 hash_function只有一个参数这一事实,我就知道您没有使用那些经过良好测试和分析的标准算法之一,而是试图使用自己的算法。别这样。

Argon2 在2015年赢得了密码哈希竞赛,据我所知,它仍然是新设计的最佳选择。它通过 K 参数(称为“ secret value”或“ key”)支持胡椒粉。我知道没有理由不用胡椒粉。在最坏的情况下,胡椒粉会随着数据库一起被破坏,你不会比如果你没有使用它更糟糕。

如果你不能使用内置的胡椒支持,你可以使用来自 这个讨论的两个建议公式之一:

Argon2(salt, HMAC(pepper, password))   or   HMAC(pepper, Argon2(salt, password))

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