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:
;
at the end of your final }
$result_array
, which is why I added the array_pop()
, assuming you only have one email record.password_verify()
call, you'll notice I switched them.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";
}
}