PHP+SQL登录系统:两种认证方案的安全与资源考量疑问
PHP+SQL登录认证方案的安全与性能抉择
我正在开发一套登录系统,当前在认证方案的选择上面临核心抉择,无法从安全与资源管理角度确定以下两种PHP+SQL认证方式哪种更优:
方案一
$user = $_POST["user"]; $pass = $_POST["pass"]; $link = new PDO('mysql....', $user, $pass); $stmt = $link->prepare('SELECT pass FROM userlogin WHERE User= ?;'); $stmt->execute([$user]); $hashed = password_hash($pass, PASSWORD_BCRYPT); if($stmt[0]['pass'] === $hashed){ password_needs_rehash($hashed, PASSWORD_BCRYPT, ['cost'=>15]); header('Location: .......'); }
方案二
$user = $_POST["user"]; $pass = $_POST["pass"]; $link = new PDO('mysql....', $user, $pass); $stmt = $link->prepare('SELECT userinfo FROM userlogin WHERE User= ? AND Pass=?;'); $stmt->execute([$user, password_hash($pass, PASSWORD_BCRYPT)]); if(count($stmt[0]) == 1){ password_needs_rehash($stmt[0]['pass'], PASSWORD_BCRYPT, ['cost'=>15]); header('Location: .......'); }
我认为第二种方案更优,理由如下:
- 若HTTPS被攻破,验证由数据库而非PHP完成,可避免数据包被嗅探;
- 成功认证后无需额外执行MySQL语句获取信息,降低处理开销。
请问该判断是否正确?从安全和性能角度是否存在遗漏的考量点?
结论:你的判断并不正确,两个方案均存在致命逻辑与安全漏洞
核心安全问题
PDO连接逻辑完全错误
两个方案里用new PDO('mysql....', $user, $pass);是致命失误:PDO连接应使用数据库专用的运维账号(比如db_app)和密码,而非用户提交的登录凭证。这种写法相当于让用户直接用自己的账号密码连数据库,恶意输入直接可能绕过认证甚至获取数据库权限。密码哈希验证逻辑失效
- 方案一:
password_hash()每次生成的哈希值都带有随机盐,你用新生成的哈希和数据库中存储的旧哈希对比,永远不可能匹配,认证逻辑完全作废。正确做法是取出数据库中的旧哈希,用password_verify($pass, $stored_hash)验证。 - 方案二:同样的问题,用户输入密码的即时哈希值,和数据库中注册时生成的哈希值(带不同盐)完全不一致,查询永远返回空结果,认证逻辑同样失效。
- 方案一:
你的安全理由不成立
若HTTPS被攻破,攻击者已经嗅探到用户提交的明文密码,此时不管在PHP还是数据库验证,都无意义——攻击者直接拿到了可用的登录凭证。而且数据库的CPU资源不适合处理密码哈希这种高计算开销的操作,应用层(PHP)横向扩容更灵活,哈希计算应放在应用层。
性能与逻辑漏洞
方案二的性能优势不存在
即使逻辑修正,方案一的流程是「查询用户哈希→PHP验证→按需重哈希更新」,方案二若要正确执行,也需要先查询用户信息(包含哈希)再验证,本质上查询次数一致,不存在减少SQL语句的优势。重哈希逻辑错误
两个方案的password_needs_rehash调用均无效:方案一用的是新生成的哈希而非数据库旧哈希;方案二查询的是userinfo,根本取不到pass字段。正确流程是验证通过后,用数据库中存储的旧哈希判断是否需要重哈希,若需要则生成新哈希并更新数据库记录。
正确的认证流程参考
// 用数据库专用账号建立连接 $link = new PDO('mysql:host=localhost;dbname=your_db', 'db_app_user', 'db_app_password'); $user = $_POST["user"]; $pass = $_POST["pass"]; // 查询用户的哈希与基础信息 $stmt = $link->prepare('SELECT id, pass, userinfo FROM userlogin WHERE User = ?;'); $stmt->execute([$user]); $userData = $stmt->fetch(PDO::FETCH_ASSOC); // 验证密码 if ($userData && password_verify($pass, $userData['pass'])) { // 检查是否需要重哈希并更新 if (password_needs_rehash($userData['pass'], PASSWORD_BCRYPT, ['cost' => 15])) { $newHash = password_hash($pass, PASSWORD_BCRYPT, ['cost' => 15]); $updateStmt = $link->prepare('UPDATE userlogin SET pass = ? WHERE id = ?;'); $updateStmt->execute([$newHash, $userData['id']]); } // 初始化会话并跳转 session_start(); $_SESSION['user_id'] = $userData['id']; header('Location: /dashboard'); exit; } else { echo "用户名或密码错误"; }
内容的提问来源于stack exchange,提问作者VTom
相关产品推荐
相关产品推荐

