应使用 password_compat 库而非手写哈希逻辑,因其由 PHP 核心开发者维护,支持 PHP ≥5.3.7,提供与原生 password_hash()/password_verify() 完全兼容的 bcrypt 实现,并确保跨版本验证一致。

PHP 的 password_hash() 和 password_verify() 在 5.5+ 原生支持,但老项目跑在 PHP 5.3/5.4 上时,不能直接用——得靠 password_compat 库补全,而不是自己手写哈希逻辑。
为什么不能自己实现 bcrypt 或重写 password_hash
自己拼接 salt、调用 crypt() 或用 hash() 做 SHA-256 + salt,既不安全也不兼容:
-
password_hash()默认用 bcrypt(PASSWORD_BCRYPT),含自适应 cost 参数和标准格式(如$2y$10$...),手写极易出错 - PHP 7.4+ 已弃用
mcrypt,5.3–5.4 缺少password_*()函数,直接调用会 fatal error - 新旧版本验证必须能互通:今天用 5.4 哈希的密码,未来升级到 8.2 还得能
password_verify()成功
用 password_compat 补齐低版本支持
这是官方维护的兼容层(由 PHP 核心开发者编写),让 PHP ≥ 5.3.7 能用上原生接口:
✅ 正确做法:
- 通过 Composer 安装:
composer require ircmaxell/password-compat(自动加载) - 或手动下载
password.php(单文件),require_once 'password.php';即可 - 之后所有代码照写
password_hash($pwd, PASSWORD_DEFAULT)和password_verify($pwd, $hash),无需条件判断 PHP 版本
⚠️ 注意:PASSWORD_DEFAULT 在 PHP 5.5–7.3 是 bcrypt,在 7.4+ 可能切为 argon2(如果编译支持),但 password_compat 里它始终固定为 bcrypt,所以跨版本验证完全一致。
立即学习“PHP免费学习笔记(深入)”;
password_hash() 的 PASSWORD_BCRYPT vs PASSWORD_DEFAULT 怎么选
对兼容性敏感的老系统,别依赖 PASSWORD_DEFAULT 的“自动升级”特性:
- 用
PASSWORD_BCRYPT显式指定算法,确保所有环境输出格式统一($2y$10$...) - 避免
cost参数设太高:PHP 5.3–5.4 的crypt()实现对高 cost 敏感,cost=12在某些旧内核可能超时;推荐['cost' => 10] - 不要用
PASSWORD_ARGON2I或PASSWORD_ARGON2ID:5.3–7.2 原生不支持,password_compat也不提供
示例:
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 10]);
迁移旧密码时怎么平滑过渡
如果数据库里存的是 md5、sha1 或自定义 salt+hash,不能直接替换为 password_hash() 结果——用户下次登录才更新:
- 读库拿到旧哈希后,先尝试
password_verify($input, $stored_hash);失败则 fallback 到旧验证逻辑(如md5($salt.$pwd)) - 验证成功后,立刻用
password_hash($input)生成新哈希,更新数据库字段 - 务必检查
$stored_hash是否以$2y$、$2a$、$2x$开头,避免对旧格式误调password_verify(它会静默返回 false)
这种渐进式升级比批量 rehash 更安全,也避免用户首次登录就卡住。
真正麻烦的不是加库,而是确认所有环境都加载了 password_compat 且没被 autoloader 覆盖;另外,有些老旧 CentOS 6 的 PHP 5.3.3 不满足最低要求(需 ≥5.3.7),得先升 PHP 小版本。











