PHP Password Hash Verifies on Hash Creation but Fails Verification with MySQL Query

Issue

I am building a dashboard system, with Apache running on a raspberry pi, and pre-generating a password and its hash for all new users.

The line I used to do this is password_hash('[email protected]', PASSWORD_DEFAULT).

Users are shown a password reset window when they first log in. I am able to successfully use password_hash() and password_verify() after the users click submit on this password reset page.

The first login works just fine but on any login attempt after logging out results in the password_verify() failing.

What I Have Checked/Tried

  • Set the password attribute in the database to varchar(255).
  • A single user row is retrieved and I can return data from it.
  • PASSWORD_DEFAULT and PASSWORD_ARGON2ID both do this.

Things I Know

  • Database is utf8mb4_unicode_ci.
  • The new password that users set are successfully pushed to the database.
    • I have added an if statement to check that the new hash can be verified and it can be.
  • Hash string matches what comes back in the SELECT, as it should.
  • I used the functions below on other projects, with PHP 7. This project is on PHP 8. (Could this be the issue?)

Password Reset Function

public function firstLoginUpdatePassword($username, $password, $confirm, $token)
{
    if ($password != $confirm)
    {
        header("Location: first-login?mismatch&token=" . $token);
        exit;
    }
    else
    {
        $newPassword = password_hash($password, PASSWORD_DEFAULT);
        $token = bin2hex(openssl_random_pseudo_bytes(16));
        try
        {
            $stmt = $this->con->prepare("UPDATE Account SET Password=:password, isFirstLogin=FALSE, Token=:token WHERE Username=:username");
            $stmt->bindparam(":username", $username);
            $stmt->bindparam(":password", $newPassword);
            $stmt->bindparam(":token", $token);

            if ($stmt->execute())
            {
                header("Location: home");
                exit;
            }
            else
            {
                header("Location: first-login?error&token=" . $token);
                exit;
            }
        }
        catch (PDOException $ex)
        {
            echo $ex->getMessage();
        }
    }
}

Login Function

public function Login($user, $pwd)
{
    try
    {
        $stmt = $this->con->prepare("SELECT Username, Password FROM Account WHERE Username=:username or Email=:username;");
        $stmt->bindparam(":username", $user);
        $stmt->execute();

        $row = $stmt->fetch(PDO::FETCH_ASSOC);

        if ($stmt->rowCount() == 1)
        {
            if (password_verify($pwd, $row['Password']))
            {
                try
                {
                    $stmt = $this->con->prepare("UPDATE Account SET LastLogin=CURRENT_TIMESTAMP WHERE Username=:username;");
                    $stmt->bindparam(":username", $row['Username']);
                    if ($stmt->execute())
                    {
                        $_SESSION['userSession'] = $row['Username'];
                        return true;
                    }
                    else
                    {
                        header("Location: login?error-other");
                        exit;
                    }
                }
                catch (PDOException $ex)
                {
                    echo $ex->getMessage();
                }
            }
            else
            {
                header("Location: login?error-credential");
                exit;
            }
        }
        else
        {
            header("Location: login?error-login");
            exit;
        }
    }
    catch(PDOException $ex)
    {
        echo $ex->getMessage();
    }
}

Solution

The solution was somewhere in the file encoding. There was a mixture of encodings on my PHP files on the server. I re-encoded all files, recursively, to UTF-8 and the issue is gone now.

Answered By – thePuterNoob

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published