php 8.5 中 password_hash() 默认使用 argon2id(若支持),否则回退 bcrypt,必须配合 password_verify() 验证,严禁手动加盐或字符串比较;存储需 varchar(255),注意空值、截断和编码问题。

php8.5 用 password_hash() 加密密码,别手写盐或算法
password_hash() 在 PHP 8.5 里仍是唯一推荐方式,它自动选 argon2id(如果编译时启用了 Argon2)或 fallback 到 bcrypt。你不需要、也不该手动调用 hash()、md5() 或拼接盐值——这些要么过时,要么易出错。
- PHP 8.5 默认启用 Argon2(前提是系统有
libsodium且编译时加了--with-password-argon2),否则退到CRYPT_BLOWFISH -
password_hash()返回的字符串自带算法、cost、salt 和哈希值,格式统一,可直接存数据库 - 不要尝试“升级”旧 hash:旧的
bcrypthash 仍能被password_verify()正确识别,无需迁移
验证密码必须用 password_verify(),别用 == 或 hash_equals() 手比对
password_verify() 不仅做恒定时间比较,还会自动解析 hash 字符串里的算法参数,并调用对应验证逻辑。直接字符串比较会失败,因为 hash 里含随机 salt,每次加密结果都不同。
常见错误现象:
- 用户登录总失败,但密码没错 → 可能误用了
==比较原始密码和 hash - 验证返回
false却查不到原因 → 没检查password_hash()是否返回false(比如传入空字符串或超长密码)
使用场景中要注意:
立即学习“PHP免费学习笔记(深入)”;
-
password_verify()第一个参数是明文密码($password),第二个是数据库里存的 hash($hash) - 它不抛异常,只返回布尔值,务必用
if (password_verify($input, $stored_hash)) { ... } - 不要对 hash 再次哈希,也不要把输入密码先 trim() 后再验证(除非业务明确要求忽略首尾空格)
PHP 8.5 的 password_hash() 参数差异:PASSWORD_ARGON2ID 是默认,但得看环境
PHP 8.5 编译时若支持 Argon2,password_hash($pwd) 无参数调用时默认用 PASSWORD_ARGON2ID;否则 fallback 到 PASSWORD_BCRYPT。不能假设一定生效。
参数影响实际行为:
- 显式指定
PASSWORD_ARGON2ID但环境不支持 → 函数返回false,不是报错 -
cost参数对 Argon2 是memory_cost、time_cost、threads,不是 bcrypt 的cost整数 - 推荐显式传 options 数组,例如:
password_hash($pwd, PASSWORD_ARGON2ID, ['memory_cost' => 65536, 'time_cost' => 4, 'threads' => 3]) - 若兼容老系统,可用
PASSWORD_BCRYPT并设['cost' => 12],但 Argon2 更抗 GPU 暴力破解
验证失败的三个高频坑:空值、截断、编码
很多“明明密码对却验不过”的问题,根源不在函数本身,而在周边处理。
- 数据库字段太短:Argon2 hash 长约 90–120 字符,
VARCHAR(255)安全;VARCHAR(60)只够 bcrypt,存 Argon2 会静默截断 → 验证必失败 - 输入为空或全空白:
password_hash('')返回false,password_verify('', $hash)总是false,需前置校验 - 字符编码混用:用户密码含 emoji 或生僻字,而数据库连接/字段用
utf8mb3→ 存入时被转成 ? 或截断 → hash 和验证用的不是同一串字节
事情说清了就结束。Argon2 是 PHP 8.5 的默认,但能不能用,得看你的 phpinfo() 里有没有 argon2 这一行;hash 存多长、怎么读出来、中间有没有 trim 或编码转换,比选哪个算法更容易翻车。











