在 PHP 中,当我们需要比较两个字符串是否相等时,常用的方法是 strcmp 函数。但随着安全需求的提升,PHP 5.6 引入了一个专门用于安全比较的函数 hash_equals。本文将详细介绍这两个函数的区别,并解释为什么在某些场景下推荐使用 hash_equals。
strcmp(string $str1, string $str2): int
strcmp 用于按字典顺序比较两个字符串。它返回一个整数值:
0 表示两个字符串完全相同
负数表示第一个字符串小于第二个字符串
正数表示第一个字符串大于第二个字符串
hash_equals(string $known_string, string $user_string): bool
hash_equals 专门用于比较两个字符串是否完全相同,且比较过程是防止计时攻击(Timing Attack)的安全方式。它返回布尔值:
true 表示两个字符串完全相等
false 表示不相等
在某些安全场景下,比如验证密码哈希、API 密钥、签名等敏感数据时,攻击者可能通过测量比较函数的执行时间,推断出字符串中相同部分的长度,进而逐步猜测正确的密钥。普通的字符串比较函数(如 strcmp)在发现第一个不同字符时就会立即返回,导致比较时间与字符串相等的部分长度有关,容易泄漏信息。
hash_equals 设计时特别考虑了防止计时攻击,其比较时间不会随两个字符串中相同部分的长度变化而变化,而是固定执行时间,从而降低泄漏信息的风险。
<?php
// 使用 strcmp 比较两个字符串
$known = 'securetoken123';
$userInput = 'securetoken124';
if (strcmp($known, $userInput) === 0) {
echo "匹配成功(strcmp)";
} else {
echo "匹配失败(strcmp)";
}
?>
<?php
// 使用 hash_equals 比较两个字符串
$known = 'securetoken123';
$userInput = 'securetoken124';
if (hash_equals($known, $userInput)) {
echo "匹配成功(hash_equals)";
} else {
echo "匹配失败(hash_equals)";
}
?>
注意,在上面代码中的字符串比较,strcmp 会在第一个不同字符时立即返回,而 hash_equals 会执行完整比较。
验证密码哈希:虽然大多数密码验证都通过 password_verify,但如果自己实现对比哈希时,推荐使用 hash_equals。
比较加密签名:例如 HMAC、JWT 等,避免密钥被猜测。
比较安全令牌:API 密钥、访问令牌、验证码等。
简单来说,任何安全相关的字符串比较,都建议使用 hash_equals。
hash_equals 要求传入的两个参数类型为字符串,且长度相同,否则会直接返回 false。
在使用 hash_equals 前,建议确保输入字符串都为字符串类型且长度一致,避免意外错误。
虽然 strcmp 是常用且功能强大的字符串比较函数,但在安全敏感的场合,hash_equals 因其防止计时攻击的特性,是更合适的选择。正确使用 hash_equals 能有效提升应用的安全性,避免潜在的攻击风险。