PHP:匹配哈希密码,用户输入不起作用[重复]

This question already has an answer here:

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.

</div>

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

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.

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){

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";
    }   
}