PHP password_verify false negative [closed] - php

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 5 years ago.
Improve this question
Background -
I have spent the last hour or so looking through various posts sites and so on to see why my password checking function has suddenly stopped working.
Every time I try to use it, it returns false, including when the inputted password is correct.
I have echoed my input and my hash, both of which match fine (by comparing the hash in the table to the one echoed).
However, when these variables are put through password_verify, it returns false 100% of the time.
What do I think is wrong? - Based on a post from an external site, I think this is due to the way that my hashedPassword variable is being parsed. When is compares the two, something goes wrong in the variable type and returns false.
What does my code and database look like? -
users table. Contains other columns.
userId | username | password
----------------------------
1 | example | temp1
Class containing the password checker function. (Have trimmed away try/catch)
public function checkPasswordCorrect($username, $password) { // Returns true is the entered password is correct and false if it isn't.
// This creates a mysqli context to run the connection through.
$mysqli = new mysqli($this->dbHost, $this->dbUsername, $this->dbPassword, $this->dbPlusPlus);
// If there is an error connecting to the database, it will be printed and the process will close.
if (mysqli_connect_errno()) {
printf("Connect failed: %s\n", mysqli_connect_error());
exit();
}
$sql = "SELECT password FROM users WHERE username='$username'";
$result = $mysqli->query($sql);
$row = $result->fetch_array(MYSQLI_ASSOC);
$hashedPassword = $row["password"];
$mysqli->close();
echo $password."<br>".$hashedPassword."<br>"; // The input is correct and the hash matches that in the table.
// We return a value of true or false, depending on the outcome of the verification.
if(password_verify($password, $hashedPassword)) {
echo "return true";
return true;
}
else {
echo "return false";
return false;
}
}
By echoing $hashedPassword just before using password_verify, the hash matches by manually checking it, as you can see by the output below.
temp0022
$2y$10$9TgjJzSaqOB
return false
What am I asking? -
Is there a better way of checking password inputs, more specifically, the way I'm pulling the information from the table (there must be some simpler way of doing this).
Is the variable parsing to blame? Is there any documentation or articles available for this topic that I haven't seen?
What am I aware of? - My usage of mysqli is shocking, I'm new to it. I am also aware that there are similar answers out there, but seem to be down to some kind of syntax error more than anything else.
Thank you for taking the time to help! :D
For all of those of you who are worried that I'm going to be hit by SQL injection attacks: I have since modified this example code
massively and am using prepared, statements. Thank you for your
concern :)

Check your table structure. Your hash field needs to be of length 60 (for PASSWORD_BCRYPT) or 255 (for PASSWORD_DEFAULT) (ref) to save the whole hash. What you got from the database is 18 characters long. You probably have it as VARCHAR(18), so it's getting truncated upon saving, which is why you're not getting a match.
Edit: Added length for the PASSWORD_DEFAULT algorithm. Thanks #Don'tPanic.

Related

Cannot get password_verify to work with password_hash (PHP, MYSQL) [closed]

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 2 years ago.
Improve this question
this is my first time posting on stackoverflow for a couple of years, so please excuse me if this isn't the correct posting format.
I'm trying to create a simple php registration and login script using mysqli. I have not yet added prepared statements to protect myself from SQL injection attacks as I want to learn the basics of submitting and receiving data from a mysql database first.
I have created a registration script that uses the password_hash function. This is completely working and is submitting to my mysql database perfectly.
However, i'm having problems with the password_verify function. I cannot get it to seem to work. If i don't use the password_hash or password_verify function in my register.php or login.php, the login works perfectly.
Although when using password_verify and password_hash together i cannot get the code to work.
Here's the code for my login.php:
<?php
include('database_connect.php');
if(isset($_POST["submit"])) {
$email = $_POST['email'];
$password = $_POST['password'];
$query = mysqli_query($conn, "SELECT * FROM users WHERE email='$email'");
$row = mysqli_fetch_array($query);
$encrypted_password = $row['password'];
if($row == 1) {
if(password_verify($password, $encrypted_password)) {
session_start();
$_SESSION['email'] = $email;
header("Location: index.php");
exit;
} else {
echo "Incorrect email or password";
}
} else {
echo "Email cannot be found, please sign up for an account";
}
}
?>
Here is the code for my register.php:
<?php
include('database_connect.php');
if(isset($_POST["submit"])) {
$firstName = $_POST['firstName'];
$lastName = $_POST['lastName'];
$email = $_POST['email'];
$password = $_POST['password'];
$confirmPassword = $_POST['confirm_password'];
$encrypted_password = password_hash($password, PASSWORD_DEFAULT);
$sql = "INSERT INTO users (firstName, lastName, email, password) VALUES ('$firstName','$lastName', '$email', '$encrypted_password')";
if ($conn->query($sql)) {
echo "<span>Account created succesfully, please <a href='login.php'>log in</a></span>";
} else {
echo "<span>There was an error, please contact site administrator</span>";
}
}
?>
I have removed the registration form validation so that the code is easier to read. If anybody could point me to the right direction or help me out, i would gladly appreciate it.
I'm pretty certain it has something to do with password hash, but i'm not sure and after countless attempts i cannot get it working.
All the best.
(in no particular order)
1)
$encrypted_password = password_hash('$password', PASSWORD_DEFAULT);
'$password' is a literal $password, because it is in single quotes PHP ignores the variable identifier ($) and prints it directly, as-is.
So your password value is always going to be "$password" and not the string that you were given in $_POST['password'].
Read about this on the original Q&A
2)
$row = mysqli_num_rows($query);
This variable is a numeric counter of rows, it is not the data within the rows.
To get the data within the rows you want mysqli_fetch_array($query) (or its similar companions).
So;
$row = mysqli_fetch_array($query);
This will be an array of data values from the database.
3)
Your code is wide open to SQL Injection attack and is completely unsafe. Yes, you're using password hash; that's a start, but you're not securing your user submitted data.
You should be using either MySQLi or PDO Prepared Statement RIGHT NOW.
4)
Headers should always be followed by die/exit commands to stop PHP continuing to execute script actions before deciding to follow the header() relocation action.
e.g.
header("Location: home.php");
exit;
5)
Whilst you may technically survive this time, on this page, you should be putting session_start(); at the very top of your PHP page, not half way down nestled in an unrelated if statement. this is inconsistent and very poor coding that will come back to bite you in the arse.
6)
Look at number 3 again, above, and absolutely do it!
7)
NEVER TRUST USER DATA. EVER.
So check and verify all values passwed from the <form> are roughly the expected shape. (filter_var($email, filter_validate_email) ;-)
After update to question.
8)
Read your PHP Error Log file. This will save you (and us all) an epic amount of time!
9)
Best practise is that if you're using PASSWORD_BCRYPT or PASSWORD_DEFAULT that you really, really should be manually setting the cost parameter yourself as the default value (10) is far too low. You should be setting it to as high a value as your server can comfortably take (14 as a guideline minimum).
Please view the details on the PHP Password_hash Manual page.
$encrypted = password_hash($password, PASSWORD_DEFAULT, ['cost' => 15]);

How to check if a row has been updated? [duplicate]

This question already has answers here:
mysqli_affected_rows in PHP insert
(3 answers)
Closed 6 years ago.
I'm working with PHP and mysqli, what the program is doing is that it is asking for a reset code and email address if the email add and reset code are found in the database it sets the password,this part of the function is working,
I need help with this part: what I need to do is tell the user if the password was set or not so if the update was successful or not.
What I'm working on:
$uinsert = "UPDATE member SET password = '$password' WHERE emailadd = '$emailadd' AND resetCode = '$resetcode'";
$update = mysqli_query($mysqli, $uinsert) or die(mysqli_error($mysqli));
if(mysqli_affected_rows($update) == 1 ){ //ifnum
header("location: ../index.php"); // Redirecting To Other Page
}
else{
echo "<script> alert('Incorrect code, try again!');</script>";
}
Note: $mysqli is my connection string
"#Fred-ii- Thank you so much that works! – Coffee coder 58 secs ago"
Use if(mysqli_affected_rows($mysqli) >0 ) or no comparison at all.
Sidenote: ==1 is only comparing for 1, as opposed to >0 which you may be trying to update more than one row. However and on the rare occasion, >0 is required where this has also happened to me before; that is the reason of my answer.
affected_rows() uses the connection, not the one for the query.
http://php.net/manual/en/mysqli.affected-rows.php
Plus, if you're storing plain text passwords, use password_hash() since it's much safer:
http://php.net/manual/en/function.password-hash.php
Sidenote: If you do decide to move over to that function, make sure that you do not manipulate the password at all. Hashing/verifying it takes care of that and you may be doing more harm than good in doing so and limiting passwords.
I.e.: A valid password of test'123 would be interpreted as test\'123 and rendering FALSE when using real_escape_string() for example.
Or you may still be using hash_hmac as per your other question Comparing/check if correct Password from mysqli database [hash_hmac]
and a prepared statement:
https://en.wikipedia.org/wiki/Prepared_statement
It is also best to add exit; after header. Otherwise, your code may want to continue to execute.
header("location: ../index.php");
exit;
Change the parameter of mysqli_affected_rows(), the parameters must be the mysql connection
mysqli_affected_rows($update)
to
mysqli_affected_rows($mysqli)
Please see this reference
https://www.w3schools.com/php/func_mysqli_affected_rows.asp
if (mysqli_affected_rows($mysqli) == 1 ) {
Because mysqli_affected_rows() does not use the query $update as its parameter, it uses the connection variable: $mysqli
pass your mysqli connection object ($connection) to mysqli_affected_rows(connection_object) to check affected rows.
connection_object is like - $con=mysqli_connect("localhost","bd_user","db_password","your_db_name");
So , code will be
if(mysqli_affected_rows($con)== 1 ){
header("location: ../index.php");
}

PHP and SQL Hashing Help: What am I doing wrong?

First off, I am fairly new to coding in general so the idea of hashing is slightly confusing. Essentially, I am trying to hash a password in order to store it in a database so I don't have the password in plain text (I am told this is the best way to do it although I don't think it would be that large of a problem if the passwords weren't hashed as this is only being used in a small group of people that I could inform not to use passwords they care about but I was still advised to do this).
I have looked up a few guides and could use some help with understanding this. I will include the way I am hashing the passwords and how I am pulling them out of the database in order to help understand this problem. Apologies ahead of time if this is a stupid question. Just a heads up, I don't really understand this which is why I am asking the question.
NOTE: Included variables such as $login_username and $login_password are being properly pulled, I just didn't want to include them as it would clutter up this mess of a post even more.
Register user (have tried password_default and password_bcrypt but I don't see a difference):
require_once 'database.php';
$hash_employee_password = password_hash($employee_password, PASSWORD_DEFAULT);
$query = "INSERT INTO employee
(employee_id, employee_first_name, employee_last_name,
employee_username, employee_email, employee_password)
VALUES
(:employee_id, :employee_first_name, :employee_last_name,
:employee_username, :employee_email, :employee_password);";
//VALUES (".$employee_id.", '" . $employee_first_name."', '" . $employee_last_name . "', '".$employee_username."', '".$employee_email."', '" . "$employee_password');";
$statement = $db->prepare($query);
$statement->bindValue(':employee_id', $employee_id);
$statement->bindValue(':employee_first_name', $employee_first_name);
$statement->bindValue(':employee_last_name', $employee_last_name);
$statement->bindValue(':employee_username', $employee_username);
$statement->bindValue(':employee_password', $hash_employee_password);
$statement->bindValue(':employee_email', $employee_email);
$statement->execute();
$statement->closeCursor();
//echo $query;
$message = 'You have been successfully registered. Contact your manager in order to request account confirmation.';
include ('success.php');
Record Login:
require_once 'database.php';
include 'register_user.php';
$pwordQuery = "SELECT employee_password from employee where employee_username = :login_username";
$pwstatement = $db->prepare($pwordQuery);
$pwstatement->bindValue(':login_username', $login_username);
$pwstatement->execute();
$result = $pwstatement->fetch();
$pwstatement->closeCursor();
echo $result[0];
if(password_verify($login_password, $result[0]))
{
echo ' TRUE';
}
else
{
echo ' FALSE ';
}
The problem is: I am entering the proper username and password, but am getting the result of "FALSE" echoed out. Let me know if you have any ideas. Disregard the fact that I have a ton of work to do such as making my queries into functions and calling them that way... That's saved for a later date.
"password column's length is what? if it's anything less than 60, mysql failed on you silently. 9 times out of 10, that's what the problem is. If so, then you'll need to start all over again by clearing your db and create a new hash. – Fred -ii- 13 mins ago"
which I was right about all along:
#Fred Well it looks like having the column length being 45 was a bad idea and that was my only problem. Thanks for the help, not entirely sure how this site works so I don't know how to give you rep or anything."
As per the manual on the password_hash() function:
http://php.net/manual/en/function.password-hash.php
PASSWORD_DEFAULT - Use the bcrypt algorithm (default as of PHP 5.5.0). Note that this constant is designed to change over time as new and stronger algorithms are added to PHP. For that reason, the length of the result from using this identifier can change over time. Therefore, it is recommended to store the result in a database column that can expand beyond 60 characters (255 characters would be a good choice).

PHP/mysqli throwing true every time [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Closed 8 years ago.
This question appears to be off-topic because it lacks sufficient information to diagnose the problem. Describe your problem in more detail or include a minimal example in the question itself.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Improve this question
I have a problem with a bit of code and connat figure why it is going wrong
//Searches the database for the username
$mysqli_username = mysqli_query($mysqli, "SELECT username FROM ajbs_users WHERE username=`".$username."`");
// if name does not exist
if ($mysqli_username == null)
{
echo "<p>Username was inccorect, or user does not exist</p>";
}
else //if username is correct
{
//finds and stores password of the user
$mysqli_userPassword = mysqli_query($mysqli, "SELECT password FROM ajbs_users WHERE password=`".$password."`");
if ($mysqli_userPassword != $password) //checks password matches from user
{
echo "<p>Password was inccorrect</p>";
}
else
{
//Login
echo "<p>You have been logged in";
// Re directs to other page after creating a session
}
}
The script keeps outputting the "Password is incorrect" statement.
There are a number of problems with your code:
mysqli_query() does not return rows from the database, it returns a resource from which you can fetch rows from the database. Or else it returns false if the query produces a parse error or an execution error. You need to check for these error cases.
Or else use mysqli_report(MYSQLI_REPORT_STRICT) to configure mysqli to throw exceptions on error (if you know how to program with exceptions).
You are using the back-ticks around the values $username and $password. Back-ticks are for delimiting identifiers like table names and column names. For string values and date values, use single quotes. Double-quotes are used the same as single quotes by default, for strings and dates. But this can change if you set MySQL into ANSI mode, then double-quotes are used like back-ticks, for identifiers. See MySQL's different quote marks
You are interpolating variables directly into your SQL expressions, which is not safe unless you've been very careful to escape them. This is how SQL injection happens. Read What is SQL injection? and How can I prevent SQL injection in PHP? You should use prepared statements and add dynamic elements to the query using parameters.
Keep in mind that if you use parameters, the quote issue disappears. You don't put any type of quotes around parameters. Using parameters is both easier and more secure than interpolating variables into strings.
And some problems with your logic or application design:
You checked if a username exists. Then if it does, you checked if a password exists. But in your code, the password is not necessarily used by the same username. Your code doesn't check that the same user has that password, only that any user has that password.
What this means is that I can hack your site if I can register one username with password I know, say 'xyzzy'. Then I can log in as any other username, using password 'xyzzy'.
It's not a good security practice to tell the user whether the username or the password is incorrect. They should only be told that the login failed. If they're a hacker, it can be useful to them to know that they're on the right track, that is, they have chosen a valid username, they just need to guess the password. You might want to log the information for your own troubleshooting, but be more vague as you report to the user.
You shouldn't store a plaintext password. Store a hash of the password. Also store a random salt per user, and use that in the hash calculation. See You're Probably Storing Passwords Incorrectly and What data type to use for hashed password field and what length?
You can do this search with one query instead of two if you compare the username and password in one query. I search for the username in the WHERE clause, and then return the result of a boolean comparison of the stored password hash against the hash of the user's input.
So here's how I would write it, keeping in mind all the points from above.
/*
* Search the database for the username, and report if the password matches
*/
$sql = "SELECT (password = SHA2(CONCAT(?, salt), 256)) AS password_matches
FROM ajbs_users WHERE username = ?";
if (($stmt = $mysqli->prepare($sql)) === false) {
trigger_error($mysqli->error, E_USER_ERROR);
}
$stmt->bind_param("ss", $password, $username);
if ($stmt->execute() === false) {
trigger_error($stmt->error, E_USER_ERROR);
}
$stmt->bind_result($password_matches);
if ($stmt->fetch()) {
if ($password_matches) {
echo "<p>You have been logged in";
// Re directs to other page after creating a session
} else {
error_log("Login attempted with user '$username', but password was incorrect.");
}
} else {
error_log("Login attempted with user '$username', but user does not exist.");
}
// Tell the user only what they need to know.
echo "<p>Login failed. Username or password was incorrect</p>";

php authentication script

I need the following authentication script finished. I am weak at php/pdo so I do not know how to ask for the number of rows equalling one and then setting the session id's from the results of the query. I need to not only set the $_SESSION['userid'] but also the ['company'] and the ['security_id'] as well from the results.
here is what I have:
$userid = $_POST['userid'];
$password = $_POST['pass'];
if ( $userid != "" || $password != "" )
{
$sql = "SELECT * FROM contractors WHERE userid = '" . $userid . "' AND password = '" . $password . "'";
$result = $dbh->query( $sql );
} else
{
echo "login failed. Your fingers are too big";
}
Optional Information:
Browser: Firefox
DO NOT EVER USE THAT CODE!
You have a very serious SQL injection open there. Every user input that you take, whether from cookies or CGI, or wherever, must be sanitized before it's used in an SQL statement. I could easily break into that system by attempting a login with an username like:
user'; UPDATE contractors SET password = '1337'
... after which I could then login as anyone. Sorry if I sound aggressive, but what that code does is like forgetting to lock the front door into your company which probably doesn't even contain an alarm system.
Note that it doesn't matter whether the input is actually coming from the user or not (perhaps it's in a pre-filled, hidden from). From the security point of view, anything that comes from anywhere outside has to be considered to contain malicious input by the user.
As far as I know, you need to use the quote function of PDO to properly sanitize the string. (In mysql, this would be done with mysql_real_escape_string().) I'm not an expert on PDO, mind you, somebody please correct if I'm wrong here.
Also you probably shouldn't store any passwords directly in the database, but rather use a hash function to create a masked password, then also create a hash from the user provided password, and match the hashes. You can use the PHP hash function to do this.
As for other issues, I don't know if the approach you have on SQL SELECT is the best approach. I would just select the corresponding user's password and try matching that in the program. I don't think there's any fault in the method you're using either, but it just doesn't seem as logical, and thus there's a greater chance of me missing some bug - which in case of passwords and logins would create a window for exploits.
To do it your way, you need to notice that the result you are getting from the PDO query is a PDOStatement, that doesn't seem to have a reliable function to diretly count the amount of result rows. What you need to use is fetchAll which returns an array of the rows, and count that. However, as I said this all feels to me like it's open for failures, so I'd feel safer checking the password in the code. There's just too much distance from the actual password matching compasion for my taste, in such a security-critical place.
So, to the get the resulting password for the userid, you can use PDOStatement's fetch() which returns the contents of the column from the result. Use for example PDO::FETCH_ASSOC to get them in an associative array based on the column names.
Here's how to fix it:
$userid_dirty = $_POST['userid'];
$password_dirty = $_POST['pass'];
$success = false; // This is to make it more clear what the result is at the end
if ($userid != "" || $password != "") {
$userid = $dbh->quote($userid_dirty);
$passwordhash = hash('sha256',$password_dirty);
$sql = "SELECT userid, passwordhash, company, security_id FROM contractors WHERE userid = ".$userid;
$result = $dbh->query( $sql );
if ($result) { // Check if result not empty, that userid exists
$result_array = $result->fetch(PDO::FETCH_ASSOC);
if ($result_array['PASSWORDHASH'] == $passwordhash) {
// login success
$success = true;
// do all the login stuff here...
// such as saving $result_array['USERID'], $result_array['COMPANY'], $result_array['SECURITY_ID'] etc.
} // else fail, wrong password
} // else fail, no such user
} else {
// fail, userid or password missing
echo ' please enter user id and password.';
}
if (!$success) {
echo ' login failed.';
}
Of course, the code can be cleaned up a bit, but that should explain what needs to be done. Note that since the password is both hashed, and never used in the SQL, it doesn't actually need cleaning. But I left it there just in case, since in the original code it was used in the query.
Note that all the code concerning storing passwords need to be changed to store the hash instead of the password. Also, it would be a very good idea to use a salt added to the password before hashing.
Also, I provided the code simply for educational purposes - I just thought that code was the clearest way to explain how to do this. So do not mistake this site as a service to request code. :)
The php manual is an excellent resource for learning PHP. It looks like you know a little SQL, and you have heard of PDO, which is a good start. If you search google for "PDO", or look in the PHP manual for the term, you'll find the PDO section of the manual. It looks like you've found the ->query function, so now you need to see what that returns. Going to the that function's manual page, we see that it returns a PDOStatement object. The word PDOStatement is helpfully linked to the relevant page in the manual, which lists the methods available on that object. There is a rowCount() method that will likely do what you want.

Categories