使用 hash_equals() 函数可以安全地比较 token,因为它执行的是恒定时间比较,能有效防止时序攻击。具体做法是将用户提交的 token 和服务器存储的 token 作为参数传入该函数,它会返回布尔值表示是否相等,从而确保比较过程的时间消耗与输入内容无关。

普通字符串比较为何存在时序攻击风险
在 PHP 中直接使用 == 或 === 运算符比较字符串时,解释器会在发现第一个不匹配的字符后立即停止比较并返回 false。这种“短路”行为导致比较耗时与两个字符串前缀匹配的长度成正比,攻击者可以通过测量响应时间的微小差异来逐字节推断出正确的 token,这就是时序攻击的原理。

<?php // 模拟服务器端存储的正确 Token
$correctToken = "a1b2c3d4e5f6";
// 模拟攻击者尝试的不同 Token
$attackerTokens = [
"x1b2c3d4e5f6", // 第1个字符不同
"a1x2c3d4e5f6", // 第2个字符不同
"a1b2x3d4e5f6" // 第3个字符不同
];
foreach ($attackerTokens as $token) {
// 使用普通比较运算符
$result = ($correctToken === $token);
echo "Comparing '$token' with '===' : ";
var_dump($result);
}
?>
上述代码展示了使用 === 进行直接比较的场景。当攻击者发送 x1b2c3d4e5f6 时,PHP 会在第一个字符处发现不匹配并立即返回 false;而发送 a1x2c3d4e5f6 时,程序会多比较一个字符才返回 false。虽然单次差异极小,但在高并发或网络延迟可控的情况下,这种时间差足以被统计出来。
hash_equals() 如何实现恒定时间比较
hash_equals() 的核心机制是遍历所有字符,无论是否提前发现不匹配,都会完成整个字符串的比较过程。它通过异或运算累积差异位,最后统一检查结果,使得比较耗时仅取决于字符串长度,而与内容无关。这彻底消除了通过时间测量推断内容的可能性。

<?php // 模拟服务器端存储的正确 Token
$correctToken = "a1b2c3d4e5f6";
// 模拟攻击者尝试的不同 Token
$attackerTokens = [
"x1b2c3d4e5f6",
"a1x2c3d4e5f6",
"a1b2x3d4e5f6"
];
foreach ($attackerTokens as $token) {
// 使用 hash_equals 进行安全比较
$result = hash_equals($correctToken, $token);
echo "Comparing '$token' with hash_equals: ";
var_dump($result);
}
?>
在上述代码中,即使 $token 的前几个字符与 $correctToken 相同,hash_equals() 仍会完整遍历整个字符串。这意味着对于任何长度的错误 token,只要长度一致,比较所花费的时间几乎完全相同,攻击者无法利用时间差来获取有效信息。
hash_equals() 对长度不一致的处理结果
当使用 hash_equals() 比较两个长度不同的字符串时,函数会直接返回 false,而不会抛出异常或继续比较。这是出于安全考虑,因为长度本身也可能泄露信息。因此,在实际应用中,应确保参与比较的两个字符串长度一致,或者在调用 hash_equals() 前先检查长度。
<?php $correctToken = "a1b2c3d4e5f6"; $shortToken = "a1b2"; $longToken = "a1b2c3d4e5f6extra"; // 长度不一致时的比较 $result1 = hash_equals($correctToken, $shortToken); $result2 = hash_equals($correctToken, $longToken); echo "Short token result: "; var_dump($result1); echo "Long token result: "; var_dump($result2); ?>
运行上述代码将输出两个 false。这表明 hash_equals() 在检测到长度不匹配时会立即终止比较并返回 false,避免了潜在的信息泄露。开发者在使用时应注意,如果业务逻辑允许不同长度的 token,需先进行长度校验,再使用 hash_equals() 进行内容比较。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











