最佳做法:腌制和胡椒粉密码?

我遇到了一个讨论,在这个讨论中,我了解到我所做的事情实际上并不是叮叮当当的密码,而是从此开始使用以下function:

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

忽略select的散列algorithm(我希望这是一个盐和辣椒的讨论,而不是具体的algorithm,但我使用的是一个安全的),这是一个安全的select,还是应该做一些不同的? 对于那些不熟悉的条款:

  • salt是随机生成的值,通常与数据库中的string一起存储,旨在使不可能使用散列表来破解密码。 由于每个密码都有自己的盐,所以他们都必须单独被强制性的破解。 然而,由于盐与密码散列存储在数据库中,数据库的妥协意味着两者都将丢失。

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

有什么我失踪,并腌制和胡椒我的密码保护我的用户的安全的最佳select? 这样做有没有潜在的安全缺陷?

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

好。 看到我需要一遍又一 遍地写这个,我会对胡椒单独做一个最后的正则答案。

胡椒明显的上升

辣椒应该使得哈希函数更安全,这似乎很明显。 我的意思是,如果攻击者只能得到你的数据库,那么你的用户密码应该是安全的,对吧? 看起来合乎逻辑,对吗?

这就是为什么很多人认为辣椒是一个好主意。 这说得通”。

辣椒的现实

在安全和密码领域,“有意义”是不够的。 有些东西必须是可certificate的, 并且是有意义的,才能被认为是安全的。 另外,它必须以可维护的方式来实现。 无法维护的最安全的系统被认为是不安全的(因为如果安全性的任何部分发生故障,整个系统就会崩溃)。

而辣椒既不适合可certificate也不适合维护的模式。

辣椒的理论问题

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

  • 将一个散列送入另一个散列可能是危险的。

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

    根据以往的经验,我们知道将一个哈希结果“喂”到另一个哈希函数中会降低总体安全性。 原因是这两个散列函数都可能成为攻击的目标。

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

    重要的是,虽然这不是什么大不了的事情,但放弃也不是小事。 encryption系统旨在避免“应该工​​作”的情况下,而是专注于“devise工作”的情况下。

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

  • 反对devise工作

    bcrypt(和其他密码散列algorithm)的devise方式是使用salt。 胡椒的概念从来没有被引入。 这可能看起来像一个小事,但事实并非如此。 原因是盐不是秘密。 这只是一个攻击者可以知道的价值。 另一方面,胡椒是一个密码的秘密。

    目前的密码散列algorithm(bcrypt,pbkdf2等)都被devise成只接受一个秘密值(密码)。 在algorithm中join另一个秘密,一直没有被研究过。

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

    因此,直到algorithm被devise和密码学家审查与秘密值(辣椒)使用,目前的algorithm不应该与他们一起使用。

  • 复杂性是安全的敌人

    信不信由你, 复杂性是安全的敌人 。 制作一个看似复杂的algorithm可能是安全的,也可能不是。 但是这个机会是不安全的。

辣椒的重要问题

  • 这是不可维护的

    你的辣椒的执行排除了旋转胡椒钥匙的能力。 由于胡椒粉用于单向函数的input,所以永远不能改变辣椒的值。 这意味着你需要拿出一些不可思议的黑客来支持重要的轮换。

    这是非常重要的,因为无论何时您存储密码机密都是必需的。 没有一个机制来轮换键(定期和违反)是一个巨大的安全漏洞。

    而你目前的胡椒方法将要求每个用户要么完全无效的密码,或者等到他们的下一次login旋转(这可能永远不会)…

    这基本上使你的方法立即不行。

  • 它需要你滚你自己的encryption

    由于目前没有一种algorithm支持辣椒的概念,因此它要求您组合algorithm或者创build新的algorithm来支持辣椒。 如果你不能立即明白为什么这是一件非常糟糕的事情:

    任何人,从最无知的业余人员到最好的密码学家,都可以创build一个他自己无法破解的algorithm。

    • 布鲁斯·施奈尔

    切勿自行encryption

更好的方式

所以,在上述所有问题中,处理这种情况有两种方式。

  • 只要使用algorithm,因为它们存在

    如果正确使用bcrypt或scrypt(成本很高),除了最薄弱的字典密码之外,所有字符密码都应该是统计安全的。 目前以成本5进行散列encryption的logging是每秒71k次散列。 那样的话,即使是6个字符的随机密码也需要数年时间才能破解。 考虑到我的最低build议成本是10,那么每秒钟的哈希值就会减less32倍。所以我们只能说每秒2200次的哈希值。 以这样的速度,甚至一些字典短语或修改可能是安全的。

    此外,我们应该检查那些在门口的弱密码类别,不允许他们进入。由于密码破解变得更加先进,密码质量要求也应该如此。 这仍然是一个统计游戏,但具有适当的存储技术和强大的密码,每个人都应该是非常安全的…

  • 在存储之前encryption输出散列

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

    我们再来看看那条线。 假设一个攻击者知道你的algorithm(这是安全所需要的,否则就是默默无闻的安全性)。 用传统的胡椒方法,攻击者可以创build一个哨兵密码,并且由于他知道盐和输出,他可以蛮力的胡椒。 好吧,这是一个远射,但它是可能的。 用密码,攻击者什么也得不到。 而且由于盐是随机的,一个哨兵密码甚至不会帮助他/她。 所以他们留下的最好的是攻击encryption的forms。 这意味着他们首先必须攻击你的encryption哈希以恢复encryption密钥,然后攻击哈希。 但是对攻击密码的研究还有很多 ,所以我们要依靠这个。

TL / DR

不要用辣椒。 有很多问题,有两个更好的方法:不使用任何服务器端的秘密(是的,没关系),并在存储之前使用分组密码encryption输出哈希。

拳头我们应该谈一下辣椒确切优势

  • 胡椒可以保护弱密码免受字典攻击,在特殊情况下,攻击者可以读取数据库(包含哈希值),但不能用胡椒进行访问。

一个典型的情况是SQL注入,丢弃备份,丢弃的服务器……这些情况并不罕见,听起来并不常见,而且通常不在您的控制之下(服务器托pipe)。 如果你使用…

  • 每个密码独特的盐
  • 像BCrypt这样的缓慢的哈希algorithm

强密码很好的保护 在这种情况下,即使知道盐时,也不可能强行使用强密码。 问题是弱密码,这是powershell字典的一部分,或者是它们的衍生物。 字典攻击会很快显示出来,因为你只能testing最常见的密码。

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

经常推荐使用胡椒粉的方法是在将密码和胡椒粉传递给哈希函数之前将其结合起来:

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

还有一种更好的方法:

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

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

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

一般来说,试图find一个散列的冲突是困难的(假设散列是安全的)。 但是,使用短哈希值,可以使用计算机将所有可能的哈希值生成到硬盘上。 这被称为彩虹表。 如果你创build一个彩虹表,你可以走出去的世界,并迅速find任何(无盐)的合理的密码散列。

胡椒点是要使彩虹表需要破解你的密码清单独特。 这样就浪费了更多的时间在攻击者身上build造彩虹桌。

然而盐的要点是要让每个用户的彩虹表对用户来说是唯一的,这进一步增加了攻击的复杂性。

真正的计算机安全性几乎不会使它(math上)变得不可能,只是在math上和物理上不切实际(例如在安全系统中,它将需要宇宙中的所有熵(以及更多)来计算单个用户的密码)。

无法看到在您的源代码中存储硬编码值具有任何安全相关性。 这是通过默默无闻的安全。

如果黑客获得你的数据库,他将能够开始暴力破解你的用户密码。 如果黑客设法破解一些密码,那么黑客就不会花费很长时间来识别你的胡椒。