password_hash不可用md5或sha1替代,因其自动加盐、选算法、适配升级;后者无盐、易爆破、不安全。必须用password_verify恒定时间比对,存储字段至少varchar(255)。

password_hash 为什么不能直接用 md5 或 sha1 替代
因为 password_hash 不只是哈希,它自动加盐、选合适算法、适配未来升级。md5 和 sha1 没盐、可暴力穷举、已被证明不安全——哪怕你手动加了随机 salt,也容易漏掉轮数控制、算法演进等细节。
- PHP 8.0+ 默认用
bcrypt(PASSWORD_DEFAULT),兼容性好且抗 GPU 破解 -
PASSWORD_ARGON2I或PASSWORD_ARGON2ID更强,但需编译支持argon2扩展,线上环境常被忽略 - 别硬编码
cost=12:太低(如 4)易被爆破,太高(如 16)可能拖慢登录接口,建议从 10–12 起测
验证密码必须用 password_verify,不能 strcmp 或 ===
直接比对哈希值会暴露时序信息,攻击者能靠响应时间差异逐步猜出哈希——password_verify 内部做了恒定时间比较,这是它不可替代的核心价值。
- 错误写法:
if ($_hash === password_hash($input, PASSWORD_DEFAULT))—— 完全无效,每次调用都生成新 hash - 正确流程:查库拿到原 hash 字符串(长度通常 60 字符),传给
password_verify($input, $stored_hash) - 如果数据库里存的是旧 md5,别试图“兼容验证”,应登录时用
password_needs_rehash检测并升级
password_needs_rehash 的真实使用场景
它不是用来“定期轮换密码”的,而是应对 PHP 版本升级或策略调整后,让老用户在下次登录时无缝迁移到新 hash 格式。
- 典型触发条件:
password_needs_rehash($hash, PASSWORD_DEFAULT, ['cost' => 12])返回 true,说明当前 hash 不符合新参数 - 只应在验证成功后调用:先
password_verify,再password_needs_rehash,最后password_hash更新数据库 - 别在注册时调用它——新用户直接用
password_hash即可;也别在没验证前就 rehash,那等于把错误密码也存进去了
常见报错和存储字段长度陷阱
最常踩的坑是数据库字段太短,导致 hash 被截断,后续永远验证失败。bcrypt 默认输出 60 字符,argon2 可达 90+,而很多人还用 VARCHAR(32) 存 md5。
立即学习“PHP免费学习笔记(深入)”;
- MySQL 建表务必:
password_hash VARCHAR(255) NOT NULL—— 留足余量,别省那点空间 - 报错
Invalid salt多半是传了空字符串或非 ASCII 字符给旧版crypt,现在应彻底弃用crypt,只用password_hash - PHP 7.4+ 开始,
password_hash对空字符串输入返回 false,别忘了检查返回值是否为 string











