I am trying to use password_verify in a project and despite using this function before without any issues, it is no longer working for me. I feel like I am overlooking something really small. Following is some of my code:
case 'Register':
$first_name = trim(filter_input(INPUT_POST, 'first_name', FILTER_SANITIZE_FULL_SPECIAL_CHARS));
$middle_name = trim(filter_input(INPUT_POST, 'middle_name', FILTER_SANITIZE_FULL_SPECIAL_CHARS));
$last_name = trim(filter_input(INPUT_POST, 'last_name', FILTER_SANITIZE_FULL_SPECIAL_CHARS));
$email = trim(filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL));
$password = trim(filter_input(INPUT_POST, 'password', FILTER_SANITIZE_FULL_SPECIAL_CHARS));
$phone = trim(filter_input(INPUT_POST, 'phone', FILTER_SANITIZE_FULL_SPECIAL_CHARS));
$email = checkEmail($email);
$password = checkPassword($password);
if (checkExistingEmail($email)){
$_SESSION['message'] = '<p class="formErrorMessage">That email address is already in use. Try logging in or using a different email.</p>';
include '../view/register.php';
exit;
}
if (!empty($phone)){
if (checkExistingPhone($phone)){
$_SESSION['message'] = '<p class="formErrorMessage">That phone number is already in use. Please use a different phone number.</p>';
include '../view/register.php';
exit;
}
}
if (empty($first_name) || empty($last_name) || empty($email) || empty($password)){
$_SESSION['message'] = '<p class="formErrorMessage">Please provide information for all required form fields.</p>';
include '../view/register.php';
exit;
}
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
$registrationOutcome = registerUser($first_name, $middle_name, $last_name, $email, $hashedPassword, $phone);
if($registrationOutcome === 1){
setcookie('first_name', $first_name, strtotime('+1 year'), '/');
$userData = getUser($email);
$_SESSION['loggedin'] = TRUE;
array_pop($userData);
$_SESSION['clientData'] = $clientData;
header('Location: /valleymusicclub/accounts/');
exit;
} else {
$_SESSION['message'] = "<p class='formErrorMessage'>Sorry, $first_name, but the registration failed. Please try again.</p>";
include '../view/login.php';
exit;
}
break;
case 'Login':
$email = filter_input(INPUT_POST, 'email', FILTER_SANITIZE_EMAIL);
$email = checkEmail($email);
$password = filter_input(INPUT_POST, 'password', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
$passwordCheck = checkPassword($password);
if (empty($email) || empty($passwordCheck)){
$_SESSION['message'] = '<p class="formErrorMessage">Please provide a valid email address and password.</p>';
include '../view/login.php';
exit;
}
$userData = getUser($email);
$passwordHash = $userData['password'];
$hashCheck = password_verify($password, $passwordHash);
if (!$hashCheck){
$_SESSION['message'] = "<p class='formErrorMessage'>Please provide a valid password. $password</p>";
include '../view/login.php';
exit;
}
$_SESSION['loggedin'] = TRUE;
array_pop($clientData);
$_SESSION['clientData'] = $clientData;
header('Location: /valleymusicclub/accounts/');
exit;
The thing that is baffling me is that this is almost the exact same code structure as another project I did a while back and used as a base, which prior project is STILL working on the same machine. After doing a var_dump on the function, I verified that it always returns false, even after ensuring multiple times that I am using the same password as originally put in the database.
I did some research looking into the common pitfalls for this issue. I verified that my database is a VARCHAR with 255 limit to account for the minimum 60 characters for default hash. I checked that the password was hashing once going into the database, which it is. I verified that the $userData['password'] variable holds the hashed password, which it does. I verified that the password I am passing through to the $password variable through the form is indeed the password that I typed in, which it is. I also made sure that I am not passing a hashed password in the first parameter of the function and that I am passing a hashed password as the second parameter. I am honestly at a loss. It just doesn't make sense that the same code works in a different project and not this one, even after quadruple checking everything that could go wrong.
Edit: I understand that filters can affect these things, but as I said before, this code is taken straight from a project that is working as intended, even with filters. Following is code that is working currently:
case 'register':
$clientFirstname = trim(filter_input(INPUT_POST, 'clientFirstname', FILTER_SANITIZE_FULL_SPECIAL_CHARS));
$clientLastname = trim(filter_input(INPUT_POST, 'clientLastname', FILTER_SANITIZE_FULL_SPECIAL_CHARS));
$clientEmail = trim(filter_input(INPUT_POST, 'clientEmail', FILTER_SANITIZE_EMAIL));
$clientPassword = trim(filter_input(INPUT_POST, 'clientPassword', FILTER_SANITIZE_FULL_SPECIAL_CHARS));
$clientEmail = checkEmail($clientEmail);
$checkPassword = checkPassword($clientPassword);
$existingEmail = checkExistingEmail($clientEmail);
// Check for existing email address in the table
if($existingEmail){
$message = '<p class="formErrorMessage">That email address already exists. Do you want to login instead?</p>';
include '../view/login.php';
exit;
}
// Check for missing data
if(empty($clientFirstname) || empty($clientLastname) || empty($clientEmail) || empty($checkPassword)){
$message = '<p class="formErrorMessage">Please provide information for all empty form fields.</p>';
include '../view/registration.php';
exit;
}
// Hash the checked password
$hashedPassword = password_hash($clientPassword, PASSWORD_DEFAULT);
// Send the data to the model
$regOutcome = regClient($clientFirstname, $clientLastname, $clientEmail, $hashedPassword);
// Check and report the result
if($regOutcome === 1){
setcookie('firstname', $clientFirstname, strtotime('+1 year'), '/');
$_SESSION['message'] = "<p class='formSuccessMessage'>Thanks for registering, $clientFirstname. Please use your email and password to login.</p>";
header('Location: /phpmotors/accounts/?action=login');
exit;
} else {
$_SESSION['message'] = "<p class='formErrorMessage'>Sorry, $clientFirstname, but the registration failed. Please try again.</p>";
include '../view/login.php';
exit;
}
break;
case 'Login':
$clientEmail = filter_input(INPUT_POST, 'clientEmail', FILTER_SANITIZE_EMAIL);
$clientEmail = checkEmail($clientEmail);
$clientPassword = filter_input(INPUT_POST, 'clientPassword', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
$passwordCheck = checkPassword($clientPassword);
// Run basic checks, return if errors
if (empty($clientEmail) || empty($passwordCheck)) {
$_SESSION['message'] = '<p class="formErrorMessage">Please provide a valid email address and password.</p>';
include '../view/login.php';
exit;
}
// A valid password exists, proceed with the login process
// Query the client data based on the email address
$clientData = getClient($clientEmail);
// Compare the password just submitted against
// the hashed password for the matching client
$hashCheck = password_verify($clientPassword, $clientData['clientPassword']);
// If the hashes don't match create an error
// and return to the login view
if(!$hashCheck) {
$_SESSION['message'] = '<p class="formErrorMessage">Please check your password and try again.</p>';
include '../view/login.php';
exit;
}
// A valid user exists, log them in
$_SESSION['loggedin'] = TRUE;
// Remove the password from the array
// the array_pop function removes the last
// element from an array
array_pop($clientData);
// Store the array into the session
$_SESSION['clientData'] = $clientData;
// Send them to the admin view
header('Location: /phpmotors/accounts/');
exit;
I also tried removing the filters, trim, etc., and it still is not working. Yes, I verified that the trimmed/filtered password is producing a string that matches my original password. I would agree that using filters before verifying that it works in the first place is the right way to go, but I have used this structure as demonstrated here and it works just fine in the prior project. I don't understand what is different between them.
Edit 2: I just added code to each of my projects to compare their outputs. Here is the code I added to the project that is working:
$clientData = getClient($clientEmail);
// var dumps for troubleshooting purposes
var_dump($clientPassword);
var_dump($clientData['clientPassword']);
// Compare the password just submitted against
// the hashed password for the matching client
$hashCheck = password_verify($clientPassword, $clientData['clientPassword']);
var_dump($hashCheck);
Following is the output: string(12) "Password123@" string(60) "$2y$10$gPmIdgHMtKEa28EagP4H3.TDZrWwoMY/CABIQleOqXUzh65/fov6W" bool(true)
Here is the code I added to the project currently having trouble:
$userData = getUser($email);
$passwordHash = $userData['password'];
var_dump($password);
var_dump($passwordHash);
$hashCheck = password_verify($password, $passwordHash);
var_dump($hashCheck);
This is the output: string(12) "Password123@" string(60) "$2y$10$8wheUb4qjxl.V1qM5EsKf.nE.MEF5wE8rnfnDApIMRjpIFxhgVt/2" bool(false)
Just a quick note: all I added were var_dumps. I did not change anything else about the code.