应直接使用 password_hash() + password_verify(),PHP 5.5+ 内置 bcrypt 支持,自动处理盐值、算法标识与成本参数,哈希字符串(如 "$2y$10$...")需存于 VARCHAR(255) 字段;推荐用 PASSWORD_DEFAULT,默认随 PHP 升级启用更优算法,显式指定 bcrypt 时 cost 设为 10–12;严禁手动 salt 或比对哈希字符串;登录成功后用 password_needs_rehash() 判断并平滑升级;需实测耗时,控制在 50–150ms 以平衡安全与性能。

直接用 password_hash() + password_verify(),别碰 crypt() 或手动 salt。 PHP 5.5+ 已内置 bcrypt 支持,无需第三方库,也不该自己实现。
用 password_hash() 生成 bcrypt 哈希
它自动处理盐值、算法标识和成本参数,返回的字符串(如 "$2y$10$...")可直接存数据库。字段长度至少设为 VARCHAR(255),因为未来算法升级可能拉长输出。
- 默认调用:
$hash = password_hash($password, PASSWORD_DEFAULT);—— 推荐,会随 PHP 升级自动切换更优算法 - 显式指定 bcrypt 并调高强度:
$hash = password_hash($password, PASSWORD_BCRYPT, ['cost' => 12]);——cost每 +1,耗时约翻倍;10–12 是当前安全与性能的合理区间 - 不要传
salt参数:PHP 8.0+ 已忽略该选项,且手动管理 salt 极易出错 - 检查返回值:若
password_hash()返回false,说明参数非法或系统不支持(极少见),需记录并告警
用 password_verify() 校验密码
它能从存储的哈希串中自动提取 salt 和 cost 参数,用完全相同的逻辑重算比对,不是“解密”,也不是字符串匹配。
- 典型用法:
if (password_verify($input, $storedHash)) { /* 登录成功 */ } - 即使哈希是
PASSWORD_ARGON2I生成的,只要用PASSWORD_DEFAULT存储,password_verify()仍能正确识别并验证 - 不要用
==或hash_equals()去比对哈希字符串本身——那会绕过 bcrypt 的 salt 和 cost 逻辑,完全失效
旧密码如何平滑升级到更高 cost
用户每次成功登录,都是重新哈希的最佳时机。靠 password_needs_rehash() 判断是否该更新,而不是批量扫库。
立即学习“PHP免费学习笔记(深入)”;
- 验证通过后加一句:
if (password_needs_rehash($storedHash, PASSWORD_DEFAULT, ['cost' => 12])) { $newHash = password_hash($password, PASSWORD_DEFAULT, ['cost' => 12]); /* 更新数据库 */ } -
password_needs_rehash()不会校验密码是否正确,只看现有哈希是否匹配新要求,所以必须在password_verify()成功后调用 - 如果数据库里混着 md5 或 sha1 的旧哈希,不能直接套用这套逻辑——得先在登录时识别出老格式,再用对应方式验证,并强制要求改密或同步迁移到新哈希
真正容易被忽略的点是:哈希强度不是越高越好。cost=15 在低配服务器上单次耗时可能超 500ms,导致登录接口变慢甚至被撞库利用为 DoS 手段。上线前务必在目标环境实测 password_hash() 耗时,控制在 50–150ms 区间较稳妥。



















