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.