PHP crypt()函数的用法讲解
在PHP开发中,数据安全至关重要,尤其是用户密码的存储。虽然PHP现在推荐使用 password_hash() 函数,但了解传统的哈希加密函数 crypt() 依然非常有必要。 crypt() 是一个基于标准UNIX DES算法的单向哈希函数,它通过引入盐值来增强加密的安全性。本文将详细讲解 crypt() 函数的用法、参数说明以及不同加密算法的示例。
一、函数语法
crypt(string $string, string $salt = ""): string
该函数接收两个参数:
$string:必需。规定要哈希的字符串(通常是明文密码)。$salt:可选。规定用于增加哈希安全性的盐值字符串。如果没有提供,PHP将每次随机生成一个盐值,这在PHP 8.0之前会导致每次调用的结果不同。
返回值:返回哈希后的字符串。如果盐值无效,函数将返回一个少于13个字符的字符串,并在结尾产生一个警告。
二、盐值详解
盐值是一个随机的数据片段,用于在哈希之前与密码进行组合,以防止彩虹表攻击。 crypt() 函数根据提供的盐值格式自动判断使用的算法。
PHP提供了一些常量用于检测系统支持哪些算法及盐值长度:
CRYPT_STD_DES:基于标准DES算法,盐值长度为2个字符。CRYPT_EXT_DES:基于扩展DES算法,盐值长度为9个字符,以下划线开头。CRYPT_MD5:基于MD5算法,盐值长度为12个字符,以$1$开头。CRYPT_BLOWFISH:基于Blowfish算法,盐值长度为22个字符,以$2a$、$2x$或$2y$开头,后跟一个两位数的cost参数。CRYPT_SHA256:基于SHA-256算法,盐值长度为16个字符,以$5$开头。CRYPT_SHA512:基于SHA-512算法,盐值长度为16个字符,以$6$开头。
三、支持算法及代码示例
1. 标准DES算法
标准DES算法使用一个2字符的盐值。盐值只能包含字母、数字、点号和斜杠。
<?php $password = 'mypassword'; $salt = 'ab'; $hashed = crypt($password, $salt); echo $hashed; ?>
2. MD5算法
MD5算法的盐值以 $1$ 开头,后面跟随最多8个字符的盐值字符串,最后以 $ 结尾。
<?php $password = 'mypassword'; $salt = '$1$salt1234$'; $hashed = crypt($password, $salt); echo $hashed; ?>
3. SHA-256算法
SHA-256的盐值以 $5$ 开头,可以包含轮数参数,例如 $5$rounds=5000$saltstring$。
<?php $password = 'mypassword'; $salt = '$5$rounds=5000$saltsalt$'; $hashed = crypt($password, $salt); echo $hashed; ?>
4. SHA-512算法
SHA-512是目前推荐的较强哈希算法之一,盐值以 $6$ 开头。
<?php $password = 'mypassword'; $salt = '$6$rounds=5000$saltsalt$'; $hashed = crypt($password, $salt); echo $hashed; ?>
5. Blowfish算法
Blowfish算法非常安全,盐值格式为 $2y$ 加上两位数的cost参数(04到31),再加上22个字符的盐值。 $2y$ 是PHP专门修复了之前高字节攻击漏洞后的标识,推荐使用。
<?php $password = 'mypassword'; // cost参数为10,22个字符的盐值 $salt = '$2y$10$usesomesillystringforsalt$'; $hashed = crypt($password, $salt); echo $hashed; ?>
四、密码验证实战
由于 crypt() 是单向哈希,无法解密。验证密码时,需要将用户输入的明文与之前存储的哈希值中的盐值部分重新组合进行哈希,然后比对结果。
值得注意的是,哈希结果的前面部分包含了完整的盐值信息,因此可以直接将原哈希结果作为新的盐值传入。
<?php
$originalPassword = 'securepass123';
// 初次加密存储的哈希值
$storedHash = crypt($originalPassword, '$6$rounds=5000$randomsalt$');
// 用户登录时输入的密码
$inputPassword = 'securepass123';
// 使用存储的哈希值作为盐值,crypt()会自动提取其中的盐值部分
$checkHash = crypt($inputPassword, $storedHash);
if (hash_equals($storedHash, $checkHash)) {
echo '密码验证成功!';
} else {
echo '密码验证失败!';
}
?>在上述代码中,我们使用了 hash_equals() 函数来比较哈希值,这可以有效防止时序攻击。如果直接使用 == 或 === 比较,攻击者可以通过响应时间的微小差异来推断出哈希的正确部分。
五、注意事项与现代替代方案
虽然 crypt() 功能强大,但手动管理盐值和算法选择容易出错。自PHP 5.5起,官方强烈建议使用 password_hash() 和 password_verify() 来替代 crypt() 。
password_hash()会自动生成安全的盐值,无需开发者干预。password_hash()默认使用bcrypt算法,且提供了简单的API。password_verify()内置了防止时序攻击的比较机制。
如果你正在开发新的项目,请务必优先选择现代密码哈希API,仅在维护老旧系统或需要高度自定义盐值格式时,才深入研究并使用 crypt() 函数。