password_hash() 在 PHP 7.4 与 8.0 间基本兼容但存在 cost 默认值(10→12)、salt 废弃、类型校验更严等关键差异,需清理旧代码并采用 password_needs_rehash() 渐进升级。

password_hash() 在 PHP 7.4 和 PHP 8.0 之间基本兼容,但存在关键行为差异和潜在陷阱,不能简单认为“换版本不改代码就能照常运行”。
默认算法与哈希格式一致,但底层默认值有隐性变化
- PHP 7.4 和 8.0 都将
PASSWORD_DEFAULT指向PASSWORD_BCRYPT(即$2y$前缀),生成的哈希字符串格式完全兼容,password_verify()可无差别校验。 - 但注意:PHP 8.0 开始,若编译时启用了 libsodium 且系统支持 Argon2,
PASSWORD_DEFAULT理论上可能切换为PASSWORD_ARGON2ID—— 实际中仅当显式传入PASSWORD_ARGON2ID才会启用;未指定算法时仍回退到 bcrypt,保持向后兼容。 - 所以只要不依赖
PASSWORD_DEFAULT的“未来算法”,旧哈希可长期验证,新生成的哈希也都能被老版本(≥7.4)的password_verify()正确识别。
cost 参数默认值不同,影响安全强度和性能
- PHP 7.4 中
PASSWORD_BCRYPT的默认cost是 10 - PHP 8.0 起提升为 12(除非显式指定
['cost' => 10])
这意味着:
- 同样用
password_hash($pwd, PASSWORD_DEFAULT),PHP 8.0 生成的哈希计算更慢、抗暴力能力更强; - 但 PHP 7.4 无法验证 PHP 8.0 生成的
cost=12哈希吗?可以 ——password_verify()不关心 cost 值是否匹配,只按哈希串内嵌参数执行对应运算; - 反过来,PHP 8.0 也能验证 PHP 7.4 生成的
cost=10哈希,完全无问题。
严格类型校验增强,运行时更“敏感”
PHP 8.0 对输入参数做更严格的类型检查,容易暴露旧代码隐患:
- 传入
null或数组给$password参数 → PHP 7.4 可能静默转成"null"或Array字符串,PHP 8.0 直接抛出TypeError -
options数组中键名大小写敏感 → 写'Cost' => 12在 PHP 7.4 被忽略,PHP 8.0 会触发E_WARNING
建议统一使用小写键名,并始终校验密码非空、非数组:
立即学习“PHP免费学习笔记(深入)”;
if (!is_string($password) || $password === '') {
throw new InvalidArgumentException('Password must be a non-empty string');
}
$hash = password_hash($password, PASSWORD_DEFAULT);salt 参数彻底废弃,旧逻辑必须清理
- PHP 7.4 已标记
salt选项为deprecated; -
PHP 8.0 完全忽略
salt选项,即使传入也无效,且不报错; - 若你项目里还保留手动传 salt 的代码(如
password_hash($p, PASSWORD_BCRYPT, ['salt' => $s])),升级到 8.0 后等同于没加盐,安全性归零。
务必删除所有 salt 选项,依赖函数自动生成随机盐 —— 这是唯一安全做法。
平滑迁移建议(兼顾新旧版本)
- 存储哈希时,优先用
PASSWORD_DEFAULT,并记录哈希前缀(如$2y$或$argon2id$),便于后续判断算法; - 登录验证统一用
password_verify(),它天然兼容所有历史 bcrypt/argon2 哈希; - 检查是否需升级旧哈希:用
password_needs_rehash($hash, PASSWORD_DEFAULT)判断是否因 cost 提升或算法演进而需要重哈希; - 升级时不要批量重算,而是在用户每次成功登录后,用新参数重新哈希并更新数据库。
不复杂但容易忽略。



















