自 2017 年以来,NIST 建议在散列记忆秘密(如密码)时使用秘密输入。通过混合秘密输入(通常称为“胡椒粉”),可以防止攻击者完全暴力破解密码散列,即使他们拥有散列和盐。例如,SQL 注入通常只影响数据库,而不影响磁盘上的文件,因此存储在配置文件中的胡椒粉对于攻击者来说仍然是无法访问的。胡椒粉必须随机生成一次,并且可以对所有用户相同。如果网站所有者做到了这一点,许多密码泄露可能已经变得毫无用处。
由于 password_hash 没有胡椒粉参数(即使 Argon2 具有“secret”参数,PHP 也不允许设置它),混合胡椒粉的正确方法是使用 hash_hmac()。php.net 的“添加注释”规则说我不能链接外部网站,所以我不能用任何链接来支持这些内容,例如 NIST、维基百科、来自 security stackexchange 网站的解释原因的帖子,或者任何内容... 您将不得不手动验证这一点。代码
// config.conf
pepper=c1isvFdxMDdmjOlvxpecFw
<?php
$pepper = getConfigVariable("pepper");
$pwd = $_POST['password'];
$pwd_peppered = hash_hmac("sha256", $pwd, $pepper);
$pwd_hashed = password_hash($pwd_peppered, PASSWORD_ARGON2ID);
add_user_to_database($username, $pwd_hashed);
?>
<?php
$pepper = getConfigVariable("pepper");
$pwd = $_POST['password'];
$pwd_peppered = hash_hmac("sha256", $pwd, $pepper);
$pwd_hashed = get_pwd_from_db($username);
if (password_verify($pwd_peppered, $pwd_hashed)) {
echo "密码匹配.";
}
else {
echo "密码不正确.";
}
?>
请注意,此代码包含一个计时攻击,它会泄露用户名是否存在。但我的注释超出了长度限制,所以我不得不删除这一段。
还要注意,如果胡椒粉泄露或可以破解,它将毫无用处。考虑它可能如何暴露,例如传递到 docker 容器的不同方法。针对破解,使用一个很长的随机生成的数值(如上面的示例所示),并在您使用干净的用户数据库进行新安装时更改胡椒粉。为现有数据库更改胡椒粉与更改其他散列参数相同:您可以将旧值包装在新值中并分层散列(更复杂),或者在有人登录时计算新的密码散列(使旧用户面临风险,因此这可能没问题取决于您升级的原因)。
为什么这有效?因为攻击者在窃取数据库后会执行以下操作
password_verify("a", $stolen_hash)
password_verify("b", $stolen_hash)
...
password_verify("z", $stolen_hash)
password_verify("aa", $stolen_hash)
等等。
(更现实地说,他们使用破解字典,但原则上,破解密码散列的方法是猜测。这就是我们使用特殊算法的原因:它们速度更慢,因此每次 verify() 操作都将更慢,因此他们每小时可以尝试的密码数量要少得多。)
现在,如果您使用胡椒粉会怎么样?现在他们需要这样做
password_verify(hmac_sha256("a", $secret), $stolen_hash)
如果没有该 $secret(胡椒粉),他们就无法进行此计算。他们将不得不这样做
password_verify(hmac_sha256("a", "a"), $stolen_hash)
password_verify(hmac_sha256("a", "b"), $stolen_hash)
...
等等,直到他们找到正确的胡椒粉。
如果您的胡椒粉包含 128 位的熵,只要 hmac-sha256 保持安全(即使 MD5 在技术上是安全的,可以在 hmac 中使用:只有它的抗碰撞性被破坏,但当然没有人会使用 MD5,因为越来越多的缺陷被发现),这将比太阳输出的能量更多。换句话说,即使给定已知的密码和盐,目前也不可能破解强度如此高的胡椒粉。