2

I am new to PHP and am trying to check whether a password provided by a user (login page) matches a hashed password stored in the db. The password in the db was hashed through $pw = password_hash($_POST["pw"], PASSWORD_BCRYPT); (the same approach I use for the user's input) and is stored in a VARCHAR(255) column.

I now tried using password_verify to compare this with the user input but am getting the below error which is caused by the else part.

Can someone tell me what I am doing wrong here ? I tried removing "== true" as well but that didn't work either.

My PHP:

$email = $_POST["email"];
$pw = password_hash($_POST["pw"], PASSWORD_BCRYPT); 

$stmt = $conn->prepare("SELECT email, pw FROM Users WHERE email = ?");
$stmt->bind_param('s', $email);
$stmt->execute();
$result = $stmt->get_result();
if(mysqli_num_rows($result) == 0){
    echo "Email has not been registered yet";
}else{
    if(password_verify($pw, $result["pw"]) == true){
        echo "Password correct";
    }else{
        echo "Password incorrect";
    }   
};

The error:

"Fatal error: Cannot use object of type mysqli_result as array..."

Update:
To me this is different to the other question referred to as possible duplicate as in my case I either get the above error or (when following Bing's approach below) the result is always "Password incorrect" - independent of the input.

Many thanks in advance.

TaneMahuta
  • 367
  • 3
  • 8
  • 17
  • 1
    `print_r($result)` dump your result , it is object , it can be retrieve via `$result->pw` – Noman Jun 24 '15 at 06:04
  • 1
    `$pw` contains a hash of the user input password. So when you do `password_veryify()`, your actually checking a hash of the password against a hash in the database. You should be checking the raw password provided by the user against the hash in the database. – Jeemusu Jun 24 '15 at 06:04
  • 1
    First rule with PHP errors: search for the error message with Google. –  Jun 24 '15 at 06:05
  • @Jeemusu The error has nothing to do with `password_hash()` and everything to do with `mysqli` –  Jun 24 '15 at 06:06
  • Thats right Hobo, but once that is solved you will notice there is infact a problem with `password_verify()`. – Jeemusu Jun 24 '15 at 06:07
  • @HoboSapiens: Thanks for this. I agree and apologise. – TaneMahuta Jun 24 '15 at 06:09
  • @Noman / Jeemusu: Thanks for this as well ! – TaneMahuta Jun 24 '15 at 06:09

4 Answers4

2

There are two issues with your code.

The first is as explained in the error message. Your returning your results as an object, not an array. You should access those values as an object:

$result->pw

The second issue is with your password_verify() function. $pw contains a hash of the user input password. So when you do password_veryify(), your actually checking a hash of the password against a hash in the database. You should be checking the raw password provided by the user against the hash in the database

Jeemusu
  • 10,415
  • 3
  • 42
  • 64
1

Your $result variable contains a mysqli_result object, not an array as you expect. Check out the Return Values section in the documentation, here: http://php.net/manual/en/mysqli.query.php

To convert it, simply call mysqli_fetch_array (which takes the mysqli_result as a parameter) and build your result. Here is what your code would look like:

$result_object = $stmt->get_result();
$result_array = array();
while($result_item = mysqli_fetch_array($result_object)) {
    array_push($result_array, $result_item);
}

if(count($result_array) == 0){
    echo "Email has not been registered yet";
}else{
    $single_entry = array_pop($result_array);
    if(isset($single_entry["pw"]) && password_verify($single_entry["pw"], $pw)){
        echo "Password correct";
    }else{
        echo "Password incorrect";
    }   
}

Several notes:

  1. You did not need the ; at the end of your final }
  2. You will be getting a two-dimensional array in $result_array, which is why I added the array_pop(), assuming you only have one email record.
  3. Your parameters were in the wrong order with your password_verify() call, you'll notice I switched them.
  4. I did NOT test this, but it should get you well on your way.
Bing
  • 3,071
  • 6
  • 42
  • 81
  • Just one final question on this: Do I even need the "== true" here ? It looks like on php.net they leave this out. – TaneMahuta Jun 24 '15 at 06:12
  • 1
    Glad to help. Beware note #2. Understanding multidimensional arrays is important. More good reading: http://www.w3schools.com/php/php_arrays_multi.asp – Bing Jun 24 '15 at 06:12
  • Thanks - will definitely have a look at this. :) And to add regarding 1) I used the semicolon here since this is part of a switch. Is it not needed then ? – TaneMahuta Jun 24 '15 at 06:13
  • 1
    Yes, you can leave out `== true` literally anywhere. Note that that's *two* `=` signs. Three is a different question: http://stackoverflow.com/questions/80646 – Bing Jun 24 '15 at 06:14
  • 1
    No, you do not need a `;` after a `}` anywhere in PHP. The `}` is like ending a paragraph, then adding the `;` is like an extra period. Switch statements probably want `break;` (which *does* end with `;`) between cases (so they do not all run in order, starting with the first match), but that's a separate operation. – Bing Jun 24 '15 at 06:18
  • Thanks again ! I just ran your code but now I am always getting "Password incorrect" even if the user enters exactly the same he used for the registration. – TaneMahuta Jun 24 '15 at 06:21
  • 1
    Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/81361/discussion-between-bing-and-tanemahuta). – Bing Jun 24 '15 at 06:25
  • Seems the chat got interrupted and I tried with the above but I am still getting the Password incorrect now even for definitely correct passwords. So it now seems to do the comparison but with the wrong result. – TaneMahuta Jun 24 '15 at 07:00
  • Try printing the values of the following to test your logic: `password_verify($single_entry["pw"], $pw)`, `$single_entry["pw"]`, `print_r($single_entry, true)`, `$_POST['pw']`. You should be able to figure it out from there. Otherwise ask a very specific question. – Bing Jun 24 '15 at 07:21
  • Thanks. I am not able to get it to work this way but I was able to figure this out now. Will post my solution as an answer and still happy to improve it further from there. – TaneMahuta Jun 24 '15 at 08:12
1

You're actually doing 2 things wrong.

Verifying the hash Note that password_hash() returns the algorithm, cost and salt as part of the returned hash. Therefore, all information that's needed to verify the hash is included in it. This allows the verify function to verify the hash without needing separate storage for the salt or algorithm information. ..from php.net

Retrieving the password from the database The mysqli_num_rows function doesn't return a password hash. Try to fetch this column.

But you're using the prepare statment, so you'll have to do it like shown here. So you would do something like:

//Remove
$pw = password_hash($_POST["pw"], PASSWORD_BCRYPT); 
$result = $stmt->get_result();

//Add
$stmt->bind_result($pw);
$stmt->fetch();

//Replace
if(mysqli_num_rows($result) == 0){
...
if(password_verify($pw, $result["pw"]) == true){
//With
if(empty($pw)){
...
if(password_verify($_POST["pw"], $pw) == true){
luukvhoudt
  • 1,726
  • 19
  • 33
  • Thanks for this ! Can you explain what I should do to cover the second part of your answer ? – TaneMahuta Jun 24 '15 at 06:22
  • 1
    @TaneMahuta what is inside $conn are you using mysqli or pdo? Depending on what you're using I can give a more detailed answer. – luukvhoudt Jun 24 '15 at 06:33
  • Thanks - I use mysqli: $conn = new mysqli($dbServer, $dbUser, $dbPass, $dbName); $conn->set_charset("utf8"); – TaneMahuta Jun 24 '15 at 06:44
  • Just to add here: This is not resolved yet. I was trying the other approach posted here but this doesn't detect the password as correct (yet). – TaneMahuta Jun 24 '15 at 07:01
  • 1
    @TaneMahuta Updated my answer check if this works for you. – luukvhoudt Jun 24 '15 at 08:01
  • Thanks a lot for this. I am not sure I fully understand this approach but I was able to figure this out now. I'll post my solution as an answer and still happy to improve it further from there. – TaneMahuta Jun 24 '15 at 08:11
1

I went through all the answers provided so far but either couldn't get them to work fully or couldn't get them to return me the correct results.

Since I am new to PHP this might be due to my lack of understanding of some parts of the provided comments and I apologise for that but I found a solution that works as intended for me.

I am sure this is not a perfect approach and I am happy to improve this further based on feedback and suggestions received - for the start it was important for me to have something running since I need to work on other parts that depend on this.

Here is the (working) solution I have so far:

$email = $_POST["email"];
$pw = $_POST["pw"]; 

$stmt = $conn->prepare("SELECT email FROM Users WHERE email = ?");
$stmt->bind_param('s', $email);
$stmt->execute();
$result = $stmt->get_result();
if(mysqli_num_rows($result) == 0){
    echo "Email has not been registered yet";
}else{
    $stmt = $conn->prepare("SELECT pw FROM Users WHERE email = ? LIMIT 1");
    $stmt->bind_param('s', $email);
    $stmt->execute();
    $result = $stmt->get_result();
    $pwHashed = $result->fetch_assoc();
    if(password_verify($pw, $pwHashed["pw"])){
        echo "Password correct";
    }else{
        echo "Password incorrect";
    }   
}
TaneMahuta
  • 367
  • 3
  • 8
  • 17