PHP密码应使用password_hash()和password_verify()实现加盐自适应慢哈希;禁用md5/sha1等快哈希;推荐PASSWORD_ARGON2ID或PASSWORD_DEFAULT;验证必须用password_verify()防时序攻击;重置token需安全生成并一次失效。

PHP 中密码不能用 md5() 或 sha1() 加密,这些是哈希且不可逆,但早已不安全;真正该做的是「加盐哈希 + 自适应慢哈希」,PHP 自带 password_hash() 和 password_verify() 就是为此设计的。
为什么不用 md5()、sha256() 或自己拼 salt?
这些函数计算太快,攻击者每秒可尝试数亿次暴力碰撞;自己生成 salt 容易出错(比如用时间戳、固定字符串),且无法随硬件升级自动增强强度。而 password_hash() 默认使用 bcrypt(PHP 7.2+ 支持 argon2i 或 argon2id),自动随机生成 salt、控制迭代轮数,并把算法、salt、轮数全编码进结果字符串里——你只需存这一串。
password_hash() 的参数怎么选?
最简用法足够安全:password_hash($password, PASSWORD_ARGON2ID)(推荐 PHP 7.3+)或 password_hash($password, PASSWORD_DEFAULT)(未来会自动升级算法)。避免硬编码 PASSWORD_BCRYPT 并手动设 cost,除非明确需要兼容旧系统。注意:PASSWORD_DEFAULT 返回的哈希字符串长度可能变化,数据库字段建议用 VARCHAR(255)。
验证时为什么不能用 == 或 strcmp()?
password_verify() 内部已做恒定时间比较,防止时序攻击。若手动比对哈希值(比如用 ===),攻击者可通过响应时间差异推断密码是否接近正确——哪怕只差一个字符。必须严格用:
if (password_verify($input, $hash_from_db)) { /* 登录成功 */ }。另外,$hash_from_db 必须完整读取(不能被截断),否则验证必然失败。
立即学习“PHP免费学习笔记(深入)”;
重置密码和临时 token 怎么处理?
重置链接里的 token 不是密码,但同样不能明文存、不能用短生命周期哈希。建议:用 random_bytes(32) 生成 token,再用 hash_equals(hash_hmac('sha256', $token, $secret_key), $stored_hmac) 验证;或直接用 password_hash($token, PASSWORD_ARGON2ID) 存储(虽非典型用途,但能防彩虹表+抗爆破)。关键是:token 用一次即失效,且过期时间别超过 1 小时。
最常被忽略的一点:password_hash() 生成的哈希里含算法标识(如 $2y$ 或 $argon2id$v=19$m=...),迁移旧系统时如果混用不同算法,验证逻辑必须保留对老哈希的支持(password_verify() 本身支持),但不能再接受新注册用户用旧算法。











