0

I cant seem to work out why password_verify is not working. Passwords uploaded are all hashed. Password verify using PDO returns nothing, I have added the passwords upload page to the bottom as there could potentially be a problem here? From my understanding $hash currently has no value when I run it in my error log. I am not user if the password verify is not working correctly or if there is an error in my code. I am thankful for your help.

  if (isset($_POST['submit'])) {
      $username = $_POST['username'];
      $password = $_POST['password'];
      $q = $handler->prepare('SELECT * FROM users WHERE username = ?');
      $q->execute(array($username));
      $result = $q -> fetch(PDO::FETCH_ASSOC);
      if ($result !== FALSE) {
          $hash_pwd = $result['password'];
          $hash = password_verify($hash_pwd, $password);
          error_log($password);
          error_log($username);
          error_log($hash_pwd);
          //error_log($hash);
          //error_log((int) $hash);
          if ($hash) {
              $username = $_SESSION['username'];
              error_log("loggedin");
              header("location:index.php");return;
          }
          else {
              echo '<p class="error-message3"><br><br>You have ented an incorrect login!<br>Please try again</p>';
          } 
      }
  }
  ?>

    if
  (isset($_POST['username']) && isset($_POST['password']))
       {
        if (empty($_POST['username']) || empty($_POST['password'])) {
              echo('<p class="error-message2">'.'Please Fill All Boxes'.'</p>');
          } 
        else {  
              $username = $_POST['username'];
              $password = password_hash($pass, PASSWORD_DEFAULT);
              $sql = "INSERT INTO users (username, password) VALUES (:username, :password)";
              $query = $handler->prepare($sql);
              $query->execute(array(
                ':username' => $username,
                ':password' => $password,
                 ));
              echo('<p class="error-message2">'.'Thank You For Your Submission!'.'</p>');
          }
}
?>
nerdychick
  • 315
  • 1
  • 3
  • 10
  • Try to compare `password_get_info($hash_pwd)` with `password_get_info(password_hash($password))` – Alexey Chuhrov Sep 18 '17 at 15:40
  • Have you read this answer: https://stackoverflow.com/questions/21741424/php-password-verify-not-working?rq=1? – WillardSolutions Sep 18 '17 at 17:40
  • I think I might have found the problem in your *upload* code: You don't seem to initialize `$pass`. Just add `$pass=$_POST['password'];` after `$username=...;`, and it hopefully will work. – Binarus Sep 21 '17 at 13:20
  • Comments are not for extended discussion; this conversation has been [moved to chat](http://chat.stackoverflow.com/rooms/154999/discussion-on-question-by-nerdychick-password-verify-not-working-php). – deceze Sep 21 '17 at 13:30
  • @Binarus have added this line, however to no success. Since adding this I no longer get a 'entered wrong password' echo when signing in. is this how u meant to change my upload code: $username = $_POST['username']; $pass=$_POST['password']; $password = password_hash($pass, – nerdychick Sep 21 '17 at 13:41
  • Let's continue on chat (follow the "moved to chat" link from deceze's comment) ... – Binarus Sep 21 '17 at 13:45
  • @Binarus This worked, the answer was changing the upload! Many thanks for the help. I am extremely grateful I have been working on this for weeks. – nerdychick Sep 21 '17 at 13:51
  • Glad to hear. With your old code, you probably have stored the hash of the empty string in the database (since this is what uninitialized variables default to if used in string context). Sorry that I didn't see it yesterday when you first showed your upload code; hope that you'll accept my answer nevertheless ... – Binarus Sep 21 '17 at 14:00
  • @Binarus, yes this really helped and maybe you could help with my next problem: https://stackoverflow.com/questions/46346160/google-recaptcha-error-logs-in-without-completing-puzzle-but-shows-tick – nerdychick Sep 21 '17 at 14:09

2 Answers2

2

Given the code from your original question and the code from your comment, you seem to use password_hash() and password_verify() correctly, and I could not detect another obvious error with the overall structure of your code, so I suspect something weird is going on with the data itself.

For example, it could be that your SELECT returns more than one row (if you haven't set a unique index on the username column, there could be two users with identical username, but different password - you did not show us the table structure / indices).

To debug this further, I suggest to alter your code like that:

if (isset($_POST['submit'])) {

    $username = $_POST['username'];
    $password = $_POST['password'];

    $q = $handler->prepare('SELECT * FROM users WHERE username = ?');
    $q->execute(array($username));

    if ($q->rowCount() > 0){

        error_log((string) ($q->rowCount()));

        $result = $q -> fetch(PDO::FETCH_ASSOC);
        $hash_pwd = $result['password'];
        $hash = password_verify($password, $hash_pwd);

        error_log($hash_pwd);
        error_log($hash);
        error_log((int) $hash);

        if ($hash == 0) {

            error_log("hashwrong");
            echo '<p class="error-message3">'.'<br>'.'<br>'."You have ented an incorrect login!     <br>Please try again.".'</p>';
        }  
        else {

            $_SESSION['username'] = $username;
            error_log("loggedin");
            header("location: index.php");return;
        } 
    }
}

Please let us know what the error_log() statements dump out. To make it readable, you could edit your question and append the results at the end (instead of putting it into a comment). But please don't alter that parts of the question which are already referenced by comments or answers (otherwise, later readers won't be able to make any sense from that all).

EDIT 1

I have noticed a possible error in your code. The reason why I did not see it immediately is that I never have used PHP.

However, you should not use ::rowCount() to check if a SELECT statement returns any rows. From the ::rowcount reference in the PDO manual:

PDOStatement::rowCount() returns the number of rows affected by the last DELETE, INSERT, or UPDATE statement executed by the corresponding PDOStatement object.

If the last SQL statement executed by the associated PDOStatement was a SELECT statement, some databases may return the number of rows returned by that statement. However, this behaviour is not guaranteed for all databases and should not be relied on for portable applications.

On the other hand, from the ::fetch reference in the PDO manual:

The return value of this function on success depends on the fetch type. In all cases, FALSE is returned on failure.

So could you please delete the existing line

$result = $q -> fetch(PDO::FETCH_ASSOC);

and replace

if ($q->rowCount() > 0) {

by

$result = $q -> fetch(PDO::FETCH_ASSOC);
if ($result !== FALSE) {

and tell us the result?

If this does not log any error message (except the PHP warning), then we know that the SELECT does not return any rows for some reason. Remember, according to the section from the manual shown above, chances are that the SELECT did not return rows, but that $q->rowCount() was greater than 0 nevertheless.

If it still does log the error messages, we'll have to do additional debugging.

EDIT 2

We now know that your SELECT returns a row and that there is no problem with fetching the data. Now I am running out of options myself.

IMHO, there is only one explanation left: $password is indeed wrong (from the code's point of view). Such a thing eventually could happen if the encoding of the POST request is wrong. To decide, we could do the following steps:

1) For testing purposes, insert a new user into the users table. This user's username and password shall contain characters from the ASCII range only. For example chose 'AAAAA...' as username (append further 'A' characters until the username is unique) and 'B' as password.

Then run your script again with that username / password, and let us know what happened.

The sense of this test is to prevent problems which might arise due to inappropriate string / POST data encoding / decoding.

2) Insert two more lines to your code:

error_log($username);
error_log($password);

Put these lines at the same place where the other error_log() calls are, and check if this really outputs exactly what you expect, i.e. exactly and literally the values you are POSTing to the script (i.e. the values you have input into your form before submitting it).

Let us know what these two lines output.

3) If the test described in 1. works:

Make sure that the HTML page from where your script is called has explicitly set the correct encoding. For example, have something like

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />;

in your HTML header (please note that this is for XHTML; the syntax or header might be different for HTML 5 or HTML 4.x; you'll have to research that).

Please let us know what happens then.

4) If the test described in 1. works:

I don't know how you actually call that script, but in any case, additionally set the correct encoding when calling the script. For example, if you are calling the script via AJAX, do something like

request.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=utf-8");

If you are calling it "directly" from a form, then do something like

<form method="post" enctype="..." accept-charset="UTF-8">

Please let us know what happens then.

5) If the test described in 1. works:

Make sure that your web server does not override the encoding which is set in the HTML pages. How you configure the server depends on the server software. For Apache, disable all AddDefaultCharset directives (by commenting them out).

Please let us know what happens then.

EDIT 3

The error is in your upload code. You are using $pass in hash_password(), but $pass is not initialized anywhere. So just add

$pass=$_POST['password'];

after the already existing line

$username=...;

Then create a new username / password combination and try to login using your current login code (but remember to check again if the parameters to password_verify() are in the right order). It should work then.

Binarus
  • 4,005
  • 3
  • 25
  • 41
  • You exactly identified my problem - password_verify was not working because the SELECT statement was returning more than one row. – Chiwda Jul 31 '23 at 06:19
1

The manual:

boolean password_verify ( string $password , string $hash )

https://php.net/password_verify

Your code:

$hash = password_verify($hash_pwd, $password);

You have simply reversed the two arguments to password_verify!

Community
  • 1
  • 1
deceze
  • 510,633
  • 85
  • 743
  • 889
  • You are right, but in the original version (which the first part of my answer shows in slightly modified form), it was the right order. – Binarus Sep 21 '17 at 13:08
  • @deceze I have tried reversing but still no luck. This is what you meant right?? $hash = password_verify($password, $hash_pwd); – nerdychick Sep 21 '17 at 13:10
  • @nerdychick That is correct. If it still doesn't work… are you sure those hashes belong to those passwords and those passwords were hashed correctly?! That hash indeed does not seem to belong to the password "test": https://3v4l.org/MFfJ4 – deceze Sep 21 '17 at 13:14
  • I have added the upload code, potentially the error could lye here? – nerdychick Sep 21 '17 at 13:22
  • Plus why are you making my question on hold? – nerdychick Sep 21 '17 at 13:26
  • @nerdychick I don't understand either why this has been put on hold. You have given a minimal example, you have given code, you have clearly stated what your problem is, where your code failed, and what you expected it to do instead. IMHO, this question and how you worded it is perfectly acceptable. – Binarus Sep 21 '17 at 13:29
  • @nerdychick We need to be able to reproduce a problem, at the moment we cannot; except for the reversed parameters which supposedly aren't the issue and the hash and password which obviously don't belong together. – Now, you're hashing `password_hash($pass, PASSWORD_DEFAULT)`. What is `$pass`?! – deceze Sep 21 '17 at 13:29
  • @deceze I think the OP forgot to initialize it - see my last comment to the question. – Binarus Sep 21 '17 at 13:30
  • @Binarus Yeah, if *that* is the problem: ***facepalm***. I would also not reopen the question for that, since it's a pretty basic mistake which is lost in the noise and probably won't ever be useful to anyone else. – deceze Sep 21 '17 at 13:34