PHP 登录验证必须用 password_hash()和 password_verify()处理密码哈希,禁用明文比对与 MD5/SHA1;须用 PDO 预处理防注入;登录后调用 session_regenerate_id(true)并设安全 Cookie 参数;需过滤输入、防爆破、时序攻击;忘记密码流程须严格校验 token。

PHP 登录验证必须校验密码哈希,不能明文比对
直接用 $_POST['password'] === $stored_password 是严重安全漏洞。PHP 自带的 password_hash() 和 password_verify() 是唯一推荐方式。数据库里存的必须是哈希值(如 $2y$10$……),不是明文或简单 MD5。
常见错误:用 md5() 或 sha1() 存密码,或在 SQL 查询里拼接用户输入导致注入。
- 注册时用
password_hash($_POST['password'], PASSWORD_ARGON2ID)(优先 Argon2,若不支持则用PASSWORD_DEFAULT) - 登录时查出用户记录后,用
password_verify($_POST['password'], $row['password_hash'])判断,而不是在 SQL 里比对 - 查询语句必须用 PDO 预处理,例如:
$stmt = $pdo->prepare("SELECT id, password_hash FROM users WHERE username = ?")
登录成功后必须重置会话并设置安全 Cookie
仅靠 session_start() 不够。PHP 默认会话容易被劫持或固定,尤其在 HTTP 环境下。
- 登录验证通过后立即调用
session_regenerate_id(true),销毁旧 session ID - 设置
session_set_cookie_params(['secure' => true, 'httponly' => true, 'samesite' => 'Strict'])(HTTPS 环境下secure必须为true) - 写入登录态到
$_SESSION['user_id']即可,不要存密码、token 或敏感字段 - 避免在 URL 中暴露 session ID,禁用
session.use_trans_sid
空用户名、SQL 注入、爆破攻击这三类请求要主动拦截
登录接口是攻击高频入口,光靠 前端 限制毫无意义。服务端必须做基础防护。
立即学习“PHP 免费学习笔记(深入)”;
- 对
$_POST['username']做长度和字符过滤:trim()+preg_match('/^[a-zA-Z0-9_]{3,20}$/', $user),拒绝空、超长或含特殊字符的输入 - 失败 5 次后,对同一 IP 或用户名启用 15 分钟临时锁定(可用 Redis 记录:
SET login_fail:192.168.1.100 1 EX 900) - 用
hash_equals()对比用户名(防时序攻击),虽然实际影响有限,但符合安全实践:hash_equals($expected_user, $input_user)
忘记密码流程不能绕过登录验证直接重置
“忘记密码”页面提交 邮箱 后,生成一次性 token 并发邮件,点击链接跳转到重置页 —— 这个重置页本身仍需服务端校验 token 有效性与时效性,且必须要求用户再次输入新密码两次。
- token 必须用
random_bytes(32)生成,存储进数据库时用hash_hmac('sha256', $token, $_ENV['APP_KEY'])保存,防止 DB 泄露直接滥用 - 重置接口(如
/reset-password)必须验证:$token是否存在、未使用、未过期(建议 1 小时)、对应邮箱是否合法 - 重置成功后立即清空该 token,并让所有该用户的现有 session 失效(更新数据库
users.last_password_reset时间戳,登录中间件检查此字段)
实际部署时,password_verify() 的性能开销可忽略,但 Argon2 参数调优(如 memory_cost=65536)会影响服务器并发能力;别为了“看起来快”而降级哈希强度。






























