2

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('Password1@', 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();
    }
}
2
  • Everything looks correct as far as the password_hash and password_verify functions. You're saying if (password_verify($pwd, $row['Password'])) is returning FALSE? If so, the only thing I can think of is your query states WHERE Username=:username or Email=:username where everything else is only WHERE Username=:username. Since PDO::FETCH_ASSOC only fetches the first entry, are there possibly two entries where the Email is the username and another record where Username is the username? Commented Jun 5, 2022 at 4:39
  • @MarshallC The if statement is returning false, correct. The login function allows a user to put their username or email address, hence the WHERE clause in that function. Everything else I have is set to update based on the username. Both attributes have unique constraints on them, and I don't run the password_verify() if more than one are returned (redundant check, now that I think about it, since I use fetch_assoc). I only have 2 records in the database for now. Very different username/email entries. Commented Jun 6, 2022 at 21:17

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.