在使用 PHP 的 crypt() 函数进行密码加密时,不少开发者会注意到一个奇怪的现象:有时候加密结果长度是 13 个字符,有时候是 20、34,甚至更多。这种长度的不一致容易让人误以为代码出了问题,或是密码被截断或加密错误。那么,这到底是怎么回事?
PHP 的 crypt() 函数是一个封装的加密接口,底层依赖于操作系统提供的加密算法。这个函数的第一个参数是要加密的字符串,第二个参数是“盐值”(salt)。而这个盐值不仅影响加密结果的唯一性,更决定了使用哪种加密算法。
echo crypt("mypassword", "salt");
如果你不给出盐值或使用的盐值格式不正确,PHP 会自动退回到较旧的加密方式,比如传统的 DES(Data Encryption Standard)算法,而这就会导致输出结果非常短,通常只有 13 个字符。
crypt() 函数支持多种算法,不同算法通过盐值的前缀来决定。下面是常见的几种盐值格式及其对应的加密算法:
算法 | 盐值格式 | 结果长度 |
---|---|---|
DES | 2个字符 | 13字符 |
MD5 | $1$... | 34字符 |
Blowfish | $2a$... / $2y$... | 60字符 |
SHA-256 | $5$... | 43字符 |
SHA-512 | $6$... | 86字符 |
所以如果你没有显式地传入带前缀的盐值,或传入了不符合格式的盐值,PHP 很可能退回使用旧的 DES 加密,而 DES 只输出 13 个字符。
例如:
// 使用 SHA-512 加密
echo crypt("mypassword", '$6$rounds=5000$usesomesillystring$');
// 输出类似于:$6$rounds=5000$usesomesill...(总长度约86)
而以下代码使用默认 DES 算法:
echo crypt("mypassword", 'sa');
// 输出类似于:sahGDf/YwKdl6(仅13个字符)
为避免这个问题,请始终使用带有明确前缀的盐值,最好是生成的随机盐,并且选用更安全的算法,如 Blowfish($2y$)或 SHA-512($6$)。
你可以这样自动生成盐值:
function generateSalt($algo = 'sha512') {
switch ($algo) {
case 'blowfish':
return '$2y$10$' . substr(str_replace('+', '.', base64_encode(random_bytes(22))), 0, 22);
case 'sha256':
return '$5$' . bin2hex(random_bytes(6));
case 'sha512':
default:
return '$6$' . bin2hex(random_bytes(6));
}
}
$password = 'mypassword';
$salt = generateSalt('sha512');
$hash = crypt($password, $salt);
echo $hash;
<?php
function hashPassword($password) {
$salt = '$2y$10$' . substr(str_replace('+', '.', base64_encode(random_bytes(22))), 0, 22);
return crypt($password, $salt);
}
$plainPassword = '123456';
$hashed = hashPassword($plainPassword);
// 假设保存到数据库中
file_put_contents('/var/www/m66.net/passwords.txt', $hashed . PHP_EOL);
// 验证密码
$inputPassword = '123456';
$isValid = crypt($inputPassword, $hashed) === $hashed;
echo $isValid ? '密码正确' : '密码错误';
?>
使用 crypt() 函数时,结果长度不同的根本原因在于“盐值”的格式决定了加密算法。为确保加密安全和一致性:
始终显式传入正确格式的盐值;
选择现代、安全的算法,如 $2y$ 或 $6$;
不要依赖默认行为或短盐值格式。
理解这些底层细节后,就可以避免在密码处理上出现隐蔽的安全风险,也能让你的加密逻辑更可控、更可靠。