自 2017 年以来,NIST 建议在对密码等记忆型密钥进行哈希处理时使用密钥输入。通过混合使用密钥输入(通常称为“胡椒粉”),即使攻击者拥有哈希值和盐值,也可以防止攻击者完全暴力破解密码哈希值。例如,SQL 注入通常只影响数据库,而不影响磁盘上的文件,因此存储在配置文件中的“胡椒粉”仍然在攻击者无法触及的范围。 “胡椒粉”必须随机生成一次,并且对所有用户都可以相同。如果网站所有者这样做,许多密码泄露本可以完全避免。
由于 `password_hash` 没有“胡椒粉”参数(即使 Argon2 有一个“secret”参数,PHP 也不允许设置它),混合使用“胡椒粉”的正确方法是使用 `hash_hmac()`。“添加注释”的 php.net 规则说我不能链接外部网站,所以我无法用 NIST、维基百科、安全 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,因为越来越多的缺陷被发现),这将比太阳输出的能量更多。换句话说,目前不可能破解这么强的“胡椒粉”,即使给出了已知的密码和盐值。