使用password_verify()验证哈希密码时如何引用数据库存储的哈希值
问题说明
当前登录校验逻辑无法正常工作的核心原因:
password_verify()第二个参数被硬编码为固定字符串d8578edf8458ce06fbc5bb76a58c5ca4,没有读取数据库查询到的当前用户对应的密码哈希值- 这个32位字符串是MD5算法生成的哈希,和
password_hash($string, PASSWORD_DEFAULT)生成的60位以上标准哈希格式完全不兼容,直接传入password_verify()会永远返回校验失败 - 现有SQL语句直接拼接用户传入的手机号/邮箱参数,存在SQL注入风险
正确实现代码
首先确认数据库用户表存储密码哈希的字段(示例用user_password作为字段名,字段类型需设置为varchar(255)保证能存下完整哈希),修改后的代码如下:
public function hashPassword($string){ return password_hash($string, PASSWORD_DEFAULT); } public function login($phonenumber, $password){ // 改用参数绑定查询,避免SQL注入 $users = DB::select("SELECT * FROM $this->tableName WHERE (user_phonenumber = ? OR user_email=?) AND user_state !='blocked'", [$phonenumber, $phonenumber]); if (count($users)>0){ $user = $users[0]; // 引用查询结果中当前用户的密码哈希字段做校验 if(password_verify($password, $user->user_password)) { @session_start(); $subscriptionModel = new SubscriptionModel(); $isRegistered = $subscriptionModel->isSubscribed($user->user_id); $userToSession = $user; $userToSession->isRegistered = $isRegistered; $_SESSION["user"] = $userToSession; return $user; } } return false; }
注意:后续新用户注册、用户修改密码的逻辑,必须统一调用
hashPassword()方法生成密码哈希后存入数据库,禁止存储明文、MD5等不安全的哈希值。
历史MD5哈希兼容方案
如果你数据库里现存的密码都是类似示例里的32位MD5哈希(也就是之前注册逻辑没有调用hashPassword()方法生成哈希),不能直接用上面的代码校验,需要做平滑迁移:
- 校验时先判断当前存储的哈希是不是MD5格式(长度32位、全十六进制字符)
- MD5格式的哈希先用
md5($password)和存储值做比对 - 比对成功后,调用
hashPassword()给当前用户生成新的标准password哈希,更新到数据库对应字段,后续就可以直接用password_verify()校验
兼容校验段示例代码:
// 替换原来的password_verify判断逻辑 $verifyPass = false; if (strlen($user->user_password) == 32 && ctype_xdigit($user->user_password)) { // 兼容旧MD5哈希校验 if (md5($password) === $user->user_password) { $verifyPass = true; // 校验通过后更新为标准哈希,完成迁移 $newHash = $this->hashPassword($password); DB::update("UPDATE $this->tableName SET user_password = ? WHERE user_id = ?", [$newHash, $user->user_id]); } } else { // 标准password哈希正常校验 $verifyPass = password_verify($password, $user->user_password); } if ($verifyPass) { // 写入session、返回用户信息等原有登录成功逻辑 }
内容的提问来源于stack exchange,提问作者little oghara
相关产品推荐
相关产品推荐

