一、md5()的基本定位
md5()是PHP的预定义字符串函数,用于计算一个字符串的MD5哈希值。MD5全称MessageDigestAlgorithm5,属于RSADataSecurity提出的一系列摘要算法。它的作用是把任意长度的输入转换成固定长度的输出,输出通常以32位十六进制字符串呈现。
哈希函数有几个基本特征:相同输入必然得到相同输出,不同输入理论上应得到不同输出,且从输出无法反推输入。md5()遵循这些特征,但它在现在安全标准下已经不再适合承担密码保护这类职责。
语法形式:
md5(string, raw);
二、参数说明
| 参数 | 说明 | 是否必需 |
|---|---|---|
| string | 要计算哈希值的字符串 | 必需 |
| raw | 指定输出格式 | 可选 |
raw参数控制返回值的格式:
-
raw=false:默认值,返回32位十六进制字符串,例如2fec392304a5c23ac138da22847f9b7c -
raw=true:返回16位原始二进制格式,内容不可直接阅读,适合需要紧凑存储或后续二进制处理的场景
raw参数容易被忽略。多数场景下用默认的十六进制字符串就够了,因为便于比较、存储和日志查看。只有在明确需要节省空间或与二进制协议对接时,才会用到原始格式。
三、返回值与特性
md5()返回计算后的哈希值,原字符串不受影响。它接受的是字符串输入,如果传入其他类型,PHP会按类型转换规则处理,这一点在严格模式下需要留意。
MD5的输出长度是固定的。十六进制格式固定32个字符,原始二进制格式固定16个字节。这个固定长度特性让它可以用于校验数据是否被改动,但不能用于还原原始内容。
一个需要明确的认识是:MD5属于快速哈希算法。它的设计目标是计算效率高,而不是抵抗破解。正因为它快,攻击者可以在单位时间内尝试大量输入,配合彩虹表等手段,短字符串的MD5值很容易被反查出来。
四、代码号学习编程实例
实例一:计算字符串的MD5值
<?php
$str = "PHP";
echo "原始字符串:" . $str . "<br>";
echo "MD5 哈希值:" . md5($str);
输出为2fec392304a5c23ac138da22847f9b7c。同一字符串每次运行结果一致,这是哈希函数的确定性决定的。
实例二:用MD5值做简单比对
<?php
$str = "PHP";
$expected = "2fec392304a5c23ac138da22847f9b7c";
if (md5($str) === $expected) {
echo "字符串与预期哈希值匹配";
}
这种写法常见于校验文件是否被完整传输,或者确认某段内容与记录一致。比较时建议用===而不是==,避免类型转换带来的意外。
实例三:对比两种输出格式
<?php
$str = "PHP";
echo "十六进制格式:" . md5($str) . "<br>";
echo "原始二进制格式:" . md5($str, true) . "<br>";
echo "原始格式长度:" . strlen(md5($str, true));
原始二进制格式在浏览器中会显示为乱码,这是正常现象,因为它本身就是字节序列而非可读文本。它的长度固定为16字节,比十六进制格式短一半。
实例四:哈希不匹配的情形
<?php
$str = "PHP";
echo "实际哈希值:" . md5($str) . "<br>";
if (md5($str) === "8b1a9953c4611296a827abf8c47804d7") {
echo "匹配成功";
} else {
echo "哈希值不一致,条件不成立";
}
8b1a9953c4611296a827abf8c47804d7是另一个字符串的MD5值,与"PHP"不匹配,所以判断分支不会进入。这个例子说明哈希比对的结果只有两种:一致或不一致。
五、本节课程知识要点
-
md5()返回固定长度的哈希值,默认32位十六进制,raw为true时返回16字节二进制。
-
相同输入得到相同输出,可用于完整性校验和内容比对。
-
MD5是快速哈希算法,计算效率高,但抗破解能力弱。
-
不应将md5()用于密码存储,原因包括计算速度快、易受彩虹表攻击、无盐值时相同密码哈希相同。
-
比较哈希值时使用
===,避免松散比较带来的类型问题。 -
raw参数按需使用,常规场景保持默认十六进制格式即可。
六、项目经验与使用边界
为什么不建议用md5()存密码。这是关于md5()最需要说清楚的一点。密码存储的核心要求是“即使数据库泄露,攻击者也难以还原出原始密码”。MD5的计算速度太快,一块普通显卡每秒可以计算数十亿次MD5,配合预先计算好的彩虹表,常见密码几乎瞬间就能被反查。加上很多项目直接md5($password)而不加盐,相同密码在数据库里哈希值一样,一旦一个被破解,其他用户也跟着受影响。
替代方案的选择。PHP提供了password_hash()和password_verify(),它们使用bcrypt或Argon2这类专门为密码存储设计的算法,自带盐值、计算成本可调,能显著提高破解的代价。新项目应当优先使用这套接口,而不是自己用md5()加盐拼接。
md5()仍然适合的场景。它并非没有用处。文件完整性校验、缓存键生成、非安全场景下的内容指纹、去重判断等,仍然可以用md5()。这些场景关注的是“内容是否变化”,而不是“能否抵抗攻击”。在这些用途下,md5()的速度反而是优点。
一个踩坑经历。早期做文件上传去重时,用md5_file()对文件内容取哈希,把哈希值作为文件名存储。后来发现两个内容不同但MD5相同的文件理论上存在碰撞可能,虽然实际概率极低,但在需要严格区分的场景下不够稳妥。后来改用sha256并配合文件大小一起判断,才更放心。这件事的启发是:MD5碰撞在普通业务中不常见,但在设计存储键或做安全判断时,选择更强的算法成本并不高。
关于raw参数的实际体会。大多数项目从头到尾用的都是默认十六进制格式,raw参数很少被用到。如果要做二进制存储,比如把哈希值存入BINARY(16)字段,raw格式能节省一半空间,但读取和调试时不够直观。取舍时看具体需求,不要为了省空间牺牲可维护性。
md5()是一个计算字符串MD5哈希值的函数,用法直接,输出格式由raw参数控制。它的价值在于快速生成内容指纹,用于校验和比对。它的局限在于安全性不足,不能承担密码保护职责。理解这两面,才能在项目中用对地方:需要校验内容时可以用,需要保护密码时应当换用password_hash()系列函数。