在 PHP 中,crypt() 是一个用于哈希加密的函数,它广泛应用于密码处理和数据加密的场景中。然而,许多开发者在使用 crypt() 函数加密相同内容时,会惊讶地发现:
这篇文章将带你深入理解这个现象背后的原因,并教你如何正确、安全地使用 crypt()。
crypt() 的本质是一个哈希函数,它不是为了加密数据,而是为了生成不可逆的哈希值,常用于密码验证。
函数原型如下:
string crypt(string $string, string $salt)
它接收两个参数:
$string:要加密的字符串。
$salt:加密时使用的“盐值”,用于影响哈希结果。
如果你传递了相同的 $salt,那么加密相同的 $string,自然会得到相同的结果。这正是你看到“加密结果总是一样”的原因。
这是因为你在调用 crypt() 时,可能使用了固定的盐值,或者干脆没有传入第二个参数 $salt。
PHP 的 crypt() 在没有提供 $salt 时,会尝试使用某些系统默认机制,但这种机制可能在一些系统上表现为“默认使用相同的 salt”,从而导致“同一个输入,总是得到相同的输出”。
看看下面的例子:
echo crypt("mypassword", "m6"); // 使用固定 salt
每次运行这段代码,无论多少次,输出都一样。这意味着,如果攻击者知道你使用的是固定的 salt,他们也可以预计算哈希值表,对你的系统进行攻击。
**正确做法是:使用随机的 salt 值。**每次为用户生成密码哈希时,都生成一个独立的 salt,并把它存储在数据库中。这样即使两个用户密码相同,他们的哈希值也不同。
PHP 推荐使用带有自动生成 salt 的方式,例如使用 password_hash() 函数,这是从 PHP 5.5+ 引入的更安全的方式:
$password = 'mypassword';
$hash = password_hash($password, PASSWORD_BCRYPT);
这个函数内部会自动为你生成一个独特的 salt,并将其附加在结果中。每次运行都会得到不同的结果:
// 每次输出都不一样
$hash1 = password_hash('mypassword', PASSWORD_BCRYPT);
$hash2 = password_hash('mypassword', PASSWORD_BCRYPT);
但它们仍然是可以通过 password_verify() 来验证原始密码的。
如果你非得用 crypt(),那就手动生成随机的 salt。下面是一个示例:
function generate_salt($length = 22) {
return substr(strtr(base64_encode(random_bytes($length)), '+', '.'), 0, $length);
}
$password = 'mypassword';
$salt = '$2y$10$' . generate_salt(); // 使用 Blowfish 算法
$hash = crypt($password, $salt);
每次调用都会生成不同的 salt,自然也会生成不同的加密结果。
加密后的结果应该存储在数据库中,用于后续验证。示例代码如下:
// 存储时
$hash = crypt($password, $salt);
// 保存到数据库
// 验证时
if (hash_equals($hash, crypt($inputPassword, $hash))) {
echo "密码正确";
} else {
echo "密码错误";
}
注意 crypt() 的第二个参数可以直接传入已加密的哈希值,PHP 会自动从中提取 salt。
crypt() 返回相同结果,是因为你用了相同的 salt。
要想安全,应使用随机 salt,或直接用 password_hash()。
不建议在新项目中继续使用 crypt(),除非有特别需求。
始终避免硬编码 salt,防止彩虹表攻击。
在开发涉及用户密码处理的系统时,始终以安全为第一要务。正确理解 crypt() 的行为,有助于你构建一个更稳固、更可靠的应用系统。