在PHP中,crypt()函数是一个用于加密密码的传统方法,它支持多种加密算法,通过传入不同格式的盐值(salt)来启用特定的加密机制。尽管近年来推荐使用更现代的密码散列API(如password_hash()),但在某些老系统中,crypt()依然被广泛使用。因此,了解如何为crypt()函数选择合适的算法和盐值格式,是保障安全性与系统兼容性的关键。
crypt()函数通过盐值的前缀识别所用的算法。以下是主流算法及其对应的盐值格式:
DES(传统算法)
盐值格式:2个字符(例如:"xy")
安全性:极低,已不推荐使用。
MD5
盐值格式:$1$ + 最多8个字符(例如:$1$abc12345)
安全性:弱,容易被暴力破解。
Blowfish
盐值格式:$2y$ + 2位cost因子 + 22位base64编码(例如:$2y$10$abcdefghijklmnopqrstuu)
安全性:高,广泛使用。
兼容性:推荐使用$2y$而不是$2a$,因为$2y$修复了历史上的安全问题。
SHA-256 和 SHA-512(glibc)
盐值格式:
SHA-256:$5$ + 可选rounds参数 + salt(例如:$5$rounds=5000$mysaltvalue)
SHA-512:$6$ + 可选rounds参数 + salt(例如:$6$mysaltvalue)
安全性:高
兼容性:在glibc支持的平台上可用(如大多数Linux系统),但Windows不支持。
选择加密算法时,应考虑以下几点:
安全性要求:如系统需抵御现代硬件的暴力破解,建议使用Blowfish($2y$)或SHA-512($6$)。
跨平台兼容性:若系统需在Linux和Windows之间迁移,应优先使用Blowfish算法,因SHA变种在Windows上不兼容。
计算成本控制:Blowfish支持cost参数(2–31),可根据服务器性能调整。例如,$2y$12$意味着2^12次迭代。
盐值用于防止相同密码产生相同的哈希,因此必须具备以下特性:
唯一性:每个用户的密码应使用不同的盐。
不可预测性:应使用安全的伪随机源生成盐。
示例代码,使用Blowfish生成带盐的哈希:
function generateSalt($cost = 10) {
$salt = substr(strtr(base64_encode(random_bytes(16)), '+', '.'), 0, 22);
return sprintf('$2y$%02d$%s$', $cost, $salt);
}
$password = 'MySecurePassword123';
$salt = generateSalt(12);
$hash = crypt($password, $salt);
echo "加密后的密码为: " . $hash;
使用crypt()进行密码校验时,应将用户输入与原哈希一起传给crypt(),如:
function verifyPassword($password, $hash) {
return crypt($password, $hash) === $hash;
}
这样可以确保校验过程使用相同的算法和盐值。
不要手动指定盐值格式不明的字符串,应使用程序自动生成的安全盐值。
避免使用DES或MD5,即使它们仍被支持。
优先迁移到password_hash()和password_verify(),这些函数由PHP维护,自动处理算法和盐值,能更好地抵抗现代攻击。
一个旧系统在用户注册时保存密码:
$password = $_POST['password'];
$salt = generateSalt(12);
$hashed = crypt($password, $salt);
// 保存 $hashed 到数据库
登录时验证密码:
$input = $_POST['password'];
$stored_hash = getPasswordFromDatabase(); // 假设已从数据库取出
if (verifyPassword($input, $stored_hash)) {
echo "登录成功";
} else {
echo "密码错误";
}
尽管crypt()函数在现代PHP中不再是首选工具,但了解其算法与盐值机制仍然重要,尤其在维护老旧系统时。推荐使用Blowfish($2y$)算法搭配安全生成的盐值,以确保在兼容性和安全性之间取得平衡。并建议长期来看将系统过渡到password_hash()这一更现代和安全的方案。