当前位置: 首页> 最新文章列表> 使用 crypt() 与 hash_equals() 安全地比较密码

使用 crypt() 与 hash_equals() 安全地比较密码

M66 2025-05-20

在构建用户认证系统时,安全地处理密码是开发者必须重视的问题。错误地比较密码,可能导致系统受到时序攻击(Timing Attacks)等安全威胁。PHP 提供了一些内建函数,可以帮助我们安全地处理密码验证。其中,crypt()hash_equals() 的组合使用,是一种相对安全的实践方式。

一、什么是 crypt() 函数?

crypt() 是 PHP 的一个加密函数,用于基于某种算法(如 bcryptSHA-512 等)对密码进行哈希加密。它的语法如下:

string crypt(string $string, string $salt)

salt 参数决定了加密算法及其行为。对于现代应用,推荐使用 bcrypt,可以通过以下方式自动生成适当的 salt

$salt = '$2y$10$' . substr(strtr(base64_encode(random_bytes(16)), '+', '.'), 0, 22);
$hash = crypt('mypassword', $salt);

二、为什么不能直接使用 ===== 来比较密码哈希?

很多初学者会写出如下代码来验证密码:

if (crypt($inputPassword, $storedHash) == $storedHash) {
    // 登录成功
}

这在功能上看似可行,但存在严重的安全隐患——时序攻击(Timing Attack)。攻击者可以通过测量程序响应时间的微小差异,逐步猜出正确的密码哈希。

三、安全的做法:使用 hash_equals()

为了解决时序攻击的问题,PHP 从 5.6 开始提供了 hash_equals() 函数,用于安全地比较两个哈希值:

bool hash_equals(string $known_string, string $user_string)

它使用恒定时间算法,不会根据字符串内容提前返回,避免了时间差泄露。

四、结合 crypt() 与 hash_equals() 的完整示例

<?php
// 假设我们从数据库获取了加密后的密码哈希
$storedHash = '$2y$10$9YzyYtVht3tcGEn.7PiF2OlRM0HDTrM7Z5D.yPi8hdm0fJeFVKH4K'; // 由 crypt() 生成的 bcrypt 哈希
$inputPassword = $_POST['password'] ?? '';

// 用 crypt() 对用户输入的密码进行哈希,使用存储哈希作为 salt
$inputHash = crypt($inputPassword, $storedHash);

// 使用 hash_equals() 安全比较
if (hash_equals($storedHash, $inputHash)) {
    echo "登录成功!";
} else {
    echo "密码错误!";
}
?>

五、推荐的现代做法(补充)

虽然 crypt() 仍然可以安全使用,但 PHP 官方更推荐使用 password_hash()password_verify(),因为它们封装了算法选择与安全比较过程,使用更加简单安全。例如:

// 创建密码哈希
$hash = password_hash('mypassword', PASSWORD_BCRYPT);

// 验证密码
if (password_verify($inputPassword, $hash)) {
    echo "验证通过";
}

但在某些场景下(如维护旧系统或有特殊需求),crypt()hash_equals() 的组合仍然是一个可接受且安全的方案。

六、总结

  • 避免使用 ===== 比较密码哈希;

  • 使用 crypt() 哈希密码时要正确选择 salt 和算法(推荐 bcrypt);

  • 使用 hash_equals() 进行安全比较,防止时序攻击;

  • 如果没有特定理由,推荐使用 password_hash()password_verify() 替代低层封装。

在处理用户密码时,安全不是可选项,而是必需项。正确的做法不仅能保护用户隐私,还能提升整个系统的抗攻击能力。