什么是 hash_equals()
函数
hash_equals()
是 PHP 中的一个内置函数,用于比较两个字符串是否相等。它的主要特点是能够以时间恒定的方式进行比较,从而防止时序攻击(Timing Attack)。
使用场景
- 密码验证:在验证用户密码时,通常会将用户输入的密码进行哈希处理,并与存储在数据库中的哈希值进行比较。使用
hash_equals()
可以确保即使攻击者通过测量响应时间来猜测密码,也不会获得有用的信息。 - CSRF 令牌验证:在验证 CSRF 令牌时,也需要确保比较操作是时间恒定的,以防止攻击者通过时序攻击猜测令牌。
- 任何需要安全比较字符串的场景:例如,API 密钥、签名验证等。
底层原理
时序攻击
时序攻击是一种侧信道攻击,攻击者通过测量操作所需的时间来推断敏感信息。在传统的字符串比较中,一旦发现不匹配的字符,比较就会立即停止。这种行为会导致比较操作的时间依赖于字符串内容,攻击者可以通过多次尝试并测量响应时间来逐步猜测正确的值。
时间恒定比较
hash_equals()
函数通过以下方式实现时间恒定的比较:
- 逐字节比较:无论字符串是否匹配,
hash_equals()
都会逐字节地比较整个字符串,直到最后一个字符。 - 固定时间执行:无论比较结果如何,
hash_equals()
的执行时间都是固定的,不会因为早期发现不匹配而提前终止。
这样,攻击者就无法通过测量响应时间来推断字符串的内容。
示例代码
密码验证
<?php
// 假设这是从数据库中获取的用户哈希密码
$storedHash = '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi'; // bcrypt 哈希
// 用户输入的密码
$userInput = 'password123';
// 对用户输入的密码进行哈希处理
$inputHash = password_hash($userInput, PASSWORD_BCRYPT);
// 使用 hash_equals() 进行安全比较
if (hash_equals($storedHash, $inputHash)) {
echo "Password is correct!";
} else {
echo "Password is incorrect!";
}
?>
CSRF 令牌验证
<?php
session_start();
// 生成并存储 CSRF 令牌
if (!isset($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32)); // 生成32字节的随机令牌
}
// 显示表单
?>
<!DOCTYPE html>
<html>
<head>
<title>CSRF Protected Form</title>
</head>
<body>
<form action="submit.php" method="post">
<input type="hidden" name="csrf_token" value="<?php echo htmlspecialchars($_SESSION['csrf_token']); ?>">
<label for="username">Username:</label>
<input type="text" id="username" name="username"><br><br>
<label for="password">Password:</label>
<input type="password" id="password" name="password"><br><br>
<input type="submit" value="Submit">
</form>
</body>
</html>
<?php
// submit.php - 处理表单提交
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['csrf_token']) && isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $_POST['csrf_token'])) {
// CSRF令牌验证成功,处理表单数据
$username = $_POST['username'];
$password = $_POST['password'];
// 这里可以进行数据库操作或其他业务逻辑
echo "Form submitted successfully! Username: $username, Password: $password";
} else {
// CSRF令牌验证失败
http_response_code(403);
echo "CSRF token validation failed. Please try again.";
}
}
?>
总结
hash_equals()
函数在 PHP 中用于安全地比较两个字符串,特别适用于密码验证、CSRF 令牌验证以及其他需要防止时序攻击的场景。它通过逐字节比较和固定时间执行来确保比较操作的时间恒定,从而提高安全性。理解 hash_equals()
的工作原理和使用方法对于编写安全的 PHP 代码至关重要。