PHP登录表单验证异常:错误场景均返回账户未确认提示
Hey Zac, let's break down why you're getting that misleading "Account not confirmed" error for every login issue—empty fields, wrong password, even non-existent usernames. The problem boils down to how your code flows through checks, plus a couple of PHP type-comparison quirks.
What's Causing the Issue?
Uninterrupted Code Flow: Right now, all your login checks run one after another, even if an error is already set. For example:
- If someone submits empty fields, you set
$error = "Please enter all fields"—but the code keeps running anyway. - It then tries to check if the username exists (which it doesn't, or is empty), but still proceeds to the
email_activecheck. - When the username doesn't exist,
SELECT email_active FROM users WHERE username = :usernamereturns no results.fetchColumn(0)givesfalse, and in PHP,false == "0"evaluates to true (thanks to loose type comparison). So your code triggers the "Account not confirmed" error, overwriting the original message.
- If someone submits empty fields, you set
Loose Type Comparisons: Using
==instead of===can lead to unexpected matches, like thefalse == "0"example above.
How to Fix It
We need to make sure that once an error is found, we stop running subsequent checks. We'll also switch to strict comparisons to avoid type-related bugs. Here's the revised code with explanations:
if(isset($_POST['doLogin'])){ $username = $_POST['login-username']; $password = $_POST['login-password']; $error = ''; // Initialize empty error to avoid undefined variable warnings // Check for empty fields first if(empty($username) || empty($password)){ $error = error("Please enter all fields"); } // Only run next check if no error exists yet if(empty($error)){ /// Check if username exists $SQLCheckLoginn = $odb -> prepare("SELECT COUNT(*) FROM `users` WHERE `username` = :username"); $SQLCheckLoginn -> execute(array(':username' => $username)); $countLoginn = $SQLCheckLoginn -> fetchColumn(0); if ($countLoginn < 1){ $SQL = $odb -> prepare("INSERT INTO `loginlogs` VALUES(:username, :ip, UNIX_TIMESTAMP(), 'XX')"); $SQL -> execute(array(':username' => $username." - does not exist",':ip' => $ip)); $error = error("The username does not exist in our system."); } } // Only check password if username exists and no prior error if(empty($error)){ $SQLCheckLogin = $odb -> prepare("SELECT COUNT(*) FROM `users` WHERE `username` = :username AND `password` = :password"); $SQLCheckLogin -> execute(array(':username' => $username, ':password' => SHA1($password))); $countLogin = $SQLCheckLogin -> fetchColumn(0); // Use strict comparison to ensure we get exactly 1 match if (!($countLogin === 1)){ $SQL = $odb -> prepare("INSERT INTO `loginlogs` VALUES(:username, :ip, UNIX_TIMESTAMP(), 'XX')"); $SQL -> execute(array(':username' => $username." - failed login",':ip' => $ip)); $error = error('The password you entered is invalid.'); } } // Check ban status only if login credentials are valid if(empty($error)){ $SQL = $odb -> prepare("SELECT `status` FROM `users` WHERE `username` = :username"); $SQL -> execute(array(':username' => $username)); $status = $SQL -> fetchColumn(0); if ($status === 1){ $SQL = $odb -> prepare("SELECT `reason` FROM `bans` WHERE `username` = :username"); $SQL -> execute(array(':username' => $username)); $ban = $SQL -> fetchColumn(0); if(empty($ban)){ $ban = "No reason given."; } $error = error('You are banned. Reason: '.htmlspecialchars($ban)); } } // Check email confirmation only if no prior errors if(empty($error)){ $SQL = $odb -> prepare("SELECT `email_active` FROM `users` WHERE `username` = :username"); $SQL -> execute(array(':username' => $username)); $fetch = $SQL -> fetchColumn(0); // Strict comparison to avoid matching false/0 incorrectly if ($fetch === "0"){ $error = error('Account not confirmed. Please contact support.'); } } // Proceed with login only if no errors if(empty($error)){ $SQL = $odb -> prepare("SELECT * FROM `users` WHERE `username` = :username"); $SQL -> execute(array(':username' => $username)); $userInfo = $SQL -> fetch(); $ipcountry = json_decode(file_get_contents("https://www.geoplugin.net/json.gp?ip=".$ip)) -> {'geoplugin_countryName'}; if (empty($ipcountry)) {$ipcountry = 'XX';} $SQL = $odb -> prepare('INSERT INTO `loginlogs` VALUES(:username, :ip, UNIX_TIMESTAMP(), :ipcountry)'); $SQL -> execute(array(':ip' => $ip, ':username' => $username, ':ipcountry' => $ipcountry)); $_SESSION['username'] = $userInfo['username']; $_SESSION['ID'] = $userInfo['ID']; $_SESSION['email'] = $userInfo['email']; setcookie("username", $userInfo['username'], time() + 720000); header('Location: dashboard'); exit; // Stop execution after redirect to prevent unwanted code runs } }
Key Changes Made:
- Error-Gated Checks: Each subsequent check is wrapped in
if(empty($error)), so we only move forward if no issues were found earlier. - Strict Comparisons: Switched from
==to===to avoid PHP's loose type matching (e.g.,false === "0"will correctly return false). - Explicit Error Initialization: Started with
$error = ''to avoid undefined variable warnings. - Exit After Redirect: Added
exit;afterheader('Location: dashboard');to ensure no code runs after the redirect.
This way, each error scenario will show its own specific message instead of being overwritten by the "Account not confirmed" error.
内容的提问来源于stack exchange,提问作者Zac Ram

