在某些情况下,辣椒可能会有所帮助。
作为一个典型示例,假设您正在构建一个 Web 应用程序。它由 webapp 代码(在一些 webapp 框架、ASP.NET MVC、Python 上的 Pyramid 中运行,没关系)和一个用于存储的 SQL 数据库组成。webapp 和 SQL DB在不同的物理服务器上运行。
针对数据库的最常见攻击是成功的 SQL 注入攻击。这种攻击不一定能访问您的 webapp 代码,因为 webapp 在不同的服务器和用户 ID 上运行。
您需要将密码安全地存储在数据库中,并提出以下形式的内容:
$hashed_password = hash( $salt . $password )
where与表示形式$salt
一起以明文形式存储在数据库中,并为每个新的或更改的密码随机选择。$hashed_password
每个密码散列方案最重要的方面是它hash
是一个缓慢的密码安全散列函数,有关更多背景知识,请参阅https://security.stackexchange.com/a/31846/10727。
那么问题是,考虑到向应用程序代码添加一个常量值几乎是零努力,并且应用程序代码通常不会在 SQL 注入攻击期间受到损害,那么下面的内容是否比上面的内容要好得多?
$hashed_password = hash( $pepper . $salt . $password )
where$salt
以明文形式存储在数据库中,并且$pepper
是以明文形式存储在应用程序代码中的常量(或配置,如果代码在多个服务器上使用或源是公共的)。
添加它$pepper
很容易——您只需在代码中创建一个常量,输入一个大的加密安全随机值(例如来自 /dev/urandom hex 或 base64 编码的 32 字节),然后在密码散列函数中使用该常量. 如果您有现有用户,则需要迁移策略,例如在下次登录时重新散列密码并将密码散列策略的版本号与散列一起存储。
回答:
如果数据库受损并不意味着应用程序受损,则使用$pepper
确实会增加密码哈希的强度。在不知道辣椒的情况下,密码仍然是完全安全的。由于密码特定的盐,您甚至无法确定数据库中的两个密码是否相同。
原因是hash($pepper . $salt . $password)
有效地构建了一个伪随机函数$pepper
作为键和$salt.$password
作为输入(对于理智hash
像带有 SHA*、bcrypt 或 scrypt 的 PBKDF2 这样的候选人)。伪随机函数的两个保证是您不能在密钥下从输出中推断出输入,并且在不知道密钥的情况下也不能从输入中推断出输出。这听起来很像哈希函数的单向属性,但不同之处在于,使用密码等低熵值,您可以有效地枚举所有可能的值并计算公共哈希函数下的图像,从而找到其值图像与原图像匹配。使用伪随机函数,如果没有密钥(即没有辣椒),您将无法这样做,因为您甚至无法计算没有密钥的单个值的图像。
$salt
如果您可以长时间访问数据库并且您仍然可以从外部正常使用应用程序,则此设置中的重要作用会发挥作用。如果没有,$salt
您可以将您控制的帐户的密码设置为已知值$passwordKnown
,并将哈希值与未知密码的密码进行比较$passwordSecret
。好像hash($pepper . $passwordKnown)==hash($pepper . $passwordSecret)
且仅当$passwordKnown==$passwordSecret
您可以将未知密码与任何选择的值进行比较(作为技术性,我假设哈希函数具有抗碰撞性)。hash($pepper . $salt1 . $passwordKnown)==hash($pepper . $salt2 . $passwordSecret)
但是当且仅当$salt1 . $passwordKnown == $salt2 . $passwordSecret
和 as$salt1
和$salt2
被随机选择为$passwordKnown
和时,你得到的盐$passwordSecret
盐永远不会相同(假设足够大的随机值,如 256 位),因此您无法再将密码相互比较。