优先用 hash(),别硬写 md5();md5() 算法固定、不可配置,官方明确不推荐用于安全场景,而 hash() 更通用、易迁移、支持多种算法且便于标准化处理。

PHP里用 hash() 还是 md5()?
直接说结论:优先用 hash(),别硬写 md5()。不是因为 md5() 不能用,而是它默认输出 32 字符十六进制字符串,算法固定、不可配置,且 PHP 官方早标记为「不推荐用于安全场景」——哪怕你只是做简单摘要,也该养成用更通用接口的习惯。
比如生成一段日志内容的摘要:
hash('sha256', $log_content)
比 md5($log_content) 多写两个字符,但后续想换算法(比如切到 sha3-256)时,只改第一个参数就行,不用动逻辑。
常见踩坑点:
立即学习“PHP免费学习笔记(深入)”;
-
hash()第二个参数必须是字符串;传数组或对象会报Warning: hash() expects parameter 2 to be string - 如果原始数据含中文或 JSON 字符串,确保编码一致(建议统一 UTF-8),否则不同环境摘要结果可能不一致
- 别把
hash_hmac()和hash()混用——前者需要密钥,适合防篡改;后者纯计算,适合去重或快速标识
怎么避免摘要重复导致的数据误判?
纯哈希值本身不具备唯一性保障,尤其当输入数据极短(比如只有数字 ID)或大量相似字符串时,碰撞概率虽低但存在。简单后台场景下,最稳妥的做法是加盐(salt)再摘要。
不需要密码学级盐值,一个固定项目标识就够了:
hash('sha256', 'myapp_v2_' . $input_data)
这样即使两个不同业务线都用 hash('sha256', $id),也不会因巧合产生相同摘要。
注意:
- 盐值别从用户输入里取(比如拼接
$_GET['s']),否则可能被利用构造碰撞 - 如果摘要用于数据库索引字段,记得字段长度够用:
CHAR(64)足够存sha256结果,CHAR(32)刚好卡死md5 - 别对空字符串单独处理——
hash('sha256', '')是合法且确定的值,可直接入库
JSON 数据摘要为什么总对不上?
后台常要摘要 API 请求体或配置项,而这些往往是 JSON 字符串。问题在于:PHP 的 json_encode() 默认不排序键名、不强制 ASCII 转义、还可能因版本差异输出空格/换行。
正确做法是标准化后再哈希:
$normalized = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_FORCE_OBJECT);<br>hash('sha256', $normalized);
关键参数说明:
-
JSON_UNESCAPED_UNICODE:避免中文被转成\uXXXX,保证跨语言摘要一致 -
JSON_UNESCAPED_SLASHES:防止/变成\/,减少无谓差异 -
JSON_FORCE_OBJECT:确保空数组[]编码为{},避免和null摘要混淆
如果还要进一步防键序影响,可用 ksort() 预处理数组,但多数后台场景没这个必要。
摘要结果要不要 Base64 编码?
不需要。Base64 是为了在文本协议里安全传输二进制数据,而 hash() 默认返回十六进制字符串(纯 ASCII),已经可读、可存储、可 URL 传递。强行 Base64 反而增加长度(比如 sha256 的 64 字符 hex 变成约 44 字符 base64),还引入额外解码步骤。
唯一例外是你要把摘要嵌进 HTTP Header 或 Cookie ——这时得避开 = 和换行,但更简单的办法是用 hash('sha256', $data, true) 拿原始二进制,再 base64_encode(),不过大多数后台连这步都省了,直接用 hex 就行。
真正容易被忽略的是:如果你把摘要存进 MySQL,字段用的是 utf8mb4,那没问题;但如果用旧表 utf8(实际是 utf8mb3),十六进制字符串虽是 ASCII,但建表时若没显式指定 COLLATE utf8_bin,比较时可能触发大小写不敏感行为——结果两个不同摘要被当成一样。



















