无尘阁日记

无尘阁日记

hash_equals这个比对的原理是啥?
2025-07-15

hash_equals() 是 PHP 提供的一个函数,专用于比较两个字符串(通常是哈希值)是否相等,其核心目的在于防止时序攻击(Timing Attack)


✅ 总结原理一句话:

hash_equals() 会逐字节地对比两个字符串的每一个字符,不受第一个不同字符位置的影响,始终耗时一致,以防止通过耗时差异推测密钥或哈希值。


☁️ 为什么要用它,而不是 ===

在普通的比较中,如:

if ($a === $b) { ... }

PHP(以及大多数语言)会在第一个字符不同的位置立即返回 false。攻击者可利用这一点——通过精确测量返回所花的时间,逐字节猜测出密钥或 token,称为:

时序攻击(Timing Attack)


⚙️ hash_equals() 的底层对比原理是这样的:

  1. 先检查两个字符串长度是否一致:

    • 长度不同,直接返回 false

  2. 若长度相等,则逐字节对比每一位字符:

    • 不管第几位不同,它都会全程跑完整个长度

  3. 用按位或(bitwise OR)把每一对字符的异或(XOR)结果合并起来:

    • 只有当所有字节相等时,最终结果为0。

简单伪代码(简化逻辑)如下:

function hash_equals($a, $b) {    if (strlen($a) !== strlen($b)) {        return false;    }    $result = 0;    for ($i = 0; $i < strlen($a); $i++) {        $result |= (ord($a[$i]) ^ ord($b[$i]));    }    return $result === 0;}
  • ord() 取字符的 ASCII 值

  • ^ 是异或运算:相同为0,不同为1

  • |= 是逐步累积的“或”,只要有不同,就让 $result 非0


🧠 举个具体应用场景:

你有一个签名机制:

$expected = hash_hmac('sha256', $data, $secret);$provided = $_GET['signature'];if (hash_equals($expected, $provided)) {    // 验证通过}

如果你用 ===,攻击者可能通过测时不断逼近正确签名;使用 hash_equals() 就能抵抗这类攻击。


🚨 注意事项:

  • hash_equals() 仅在 PHP 5.6+ 提供。

  • 两个参数都必须是字符串,否则会抛出警告。

  • 只能防止时序攻击,但无法防止其它逻辑漏洞


🪞延伸阅读推荐:

如夜话,至此。