I have a problem with trying to login using the saved hash from my database, I save my password the following way, which works fine:
adduser($conn, '3', $username, $password);
This calls the following function:
function adduser ($conn, $level, $username, $password)
{
$password = mysqli_real_escape_string($conn, $password);
$password = mysqli_real_escape_string($conn, $username);
$password = password_hash($password, PASSWORD_BCRYPT);
$user = "INSERT INTO users (level, username, password)
VALUES ('$level', '$username', '$password')";
mysqli_query($conn, $user) or die (mysqli_error($conn));
}
My password field is a CHAR(60) so the stored password hash should be the right size.
When I try to login I call this function:
if (login($conn, $username, $password) === true){
}
Which exists here:
function login ($conn, $username, $password)
{
$password = mysqli_real_escape_string($conn, $password);
$username = mysqli_real_escape_string($conn, $username);
$query = "SELECT password FROM `users` WHERE username='$username'";
$result = mysqli_query($conn, $query) or die(mysqli_error($conn));
$row = mysqli_fetch_array($result, MYSQLI_ASSOC);
$hash = $row["password"];
$verify = password_verify($password, $hash);
if ($verify)
{
return true;
}
else
{
return false;
}
}
My issue is that it never returns true or false, which makes it impossible for me to login...
Extra:
It succesfully post to database
I also tried running this, which succesfully posted the data from my database
$query = "SELECT password FROM `users` WHERE username='$username'";
$result = mysqli_query($conn, $query) or die(mysqli_error($conn));
$row = mysqli_fetch_array($result, MYSQLI_ASSOC);
echo $row["password"];
Update
Doing this:
echo '<br/>';
echo $hash;
echo '<br/>';
echo $password;
Gives me the following output:
$2y$10$OfJhVve4GMZRfjfelb8sNOJ7EN5NAAGOmsN6OS/SC7PZGU5mDNOou
hej
Which matches the password in my database
$2y$10$OfJhVve4GMZRfjfelb8sNOJ7EN5NAAGOmsN6OS/SC7PZGU5mDNOou
After testing your entire code, I have come to the following conclusion.
The problem here is that you are escaping the password while inserting it into your database, which is something I did raise in comments from the beginning.
"side note: you shouldn't escape a password/hash function, passwords such as 123'\abc< are perfectly valid and will be modified on insertion."
$password = mysqli_real_escape_string($conn, $username);
Side note for ^ - Consult Edit #2 below, near "However...":
Simply don't use it, just keep/use the assignment normally.
Both password_hash() and password_verify() do their job, so there's no need to escape passwords.
You will need to remove it from the code that you used to insert it into the database with, and start over again with a new set of hashes.
That escaping function is most likely adding a character during insertion.
Side note: Just for the record, my password column is VARCHAR, yet that shoulnd't be a difference from your CHAR (Edit: consult footnote). If it is then ALTER your column to be VARCHAR.
The manual on password_hash() though, suggests using 255 for a length, being a good bet.
http://php.net/manual/en/function.password-hash.php
Edit footnote:
As per a comment I posted beneath my answer.
It looks to have a difference. This Q&A What's the difference between VARCHAR and CHAR? shows it, as per the accepted answer
VARCHAR is variable-length.
CHAR is fixed length.
https://stackoverflow.com/a/1885635/1415724
Edit #2:
After further testing to see if it made a difference by ALTER'ing the password column from VARCHAR(255) to CHAR(60) made a difference; it did not.
Tests performed:
Inserted a new hash without the escaping function and verifying: TRUE.
Inserted a new hash with the escaping function and verifying: FALSE.
Therefore and as I stated originally; the fault lies with the use of mysqli_real_escape_string().
However and going over your code again, this line:
$password = mysqli_real_escape_string($conn, $username);
You were using the $username variable here which also accounts for the wrong value being inserted in the database. All of these put together were the problems from the get go.
Related
I set up a dev server and I installed nginx with php-fpm and 7.2 php (I installed all the necessary php packages).
The time has come to upgrade the security of all logins, using the argon2i algo.
So, I tried this (test code in dev enviroment. The security in the code, will come later):
require('connector.php');
$usr_u = $_POST['username'];
$psw_u = $_POST['password'];
$usr = mysqli_real_escape_string($conn, $usr_u);
$psw = mysqli_real_escape_string($conn, $psw_u);
$f_pass = password_hash($psw, PASSWORD_ARGON2I);
$result = "SELECT `username` FROM `users` WHERE username = '$usr'";
$tbl = mysqli_query($conn, $result);
$table = $tbl->fetch_assoc();
$m_user = $table['username'];
if ($m_user == $usr)
{
//correct username
echo 'Correct username!<br>';
$result = "SELECT `password` FROM `users` WHERE username = '$m_user'";
$tbl = mysqli_query($conn, $result);
$table = $tbl->fetch_assoc();
$m_pass = $table['password'];
if (password_verify($m_pass,$f_pass)) //always returns false
{
echo 'Password correct!<br>';
}
else
{
echo 'Wrong password!<br>';
}
}
else
{
echo 'Wrong username!<br>';
}
//close connection
mysqli_close($conn);
I always get "Correct username!" and "Wrong password!". I used echo on hashed password from the DB and from the input and I see that every time the hashed password is different. I assume that the hashing process, include using random salt and there is my issue.
As far as I understand, the random salt is necessary in order to retain the security of the hashing.
Can you please point me to the right direction, on how to solve this? I have the hashed password in my DB and I can't figure out a way to check the input password against the one in my DB (using password_verify and hash_equals).
Thank everyone in advance for the help.
Look at the documentation for password_verify:
bool password_verify ( string $password , string $hash )
The first argument is the password but you are passing it the hash you want to compare it to.
The second argument is the hash you want to compare it to but you are passing it a new hash created from user input.
password_verify($_POST['password'], $m_pass)
I'm trying to me a page more secure and I started with the password encrypting part of it. I'm trying to implement password_hash + password verify, but so far I've been unsuccessful to make the whole thing work. So, here it is in my login area:
$username = mysqli_real_escape_string($connection, $_POST['username']);
$password = mysqli_real_escape_string($connection, $_POST['password']);
$query = "SELECT username, password FROM `users` WHERE username='$username' and user_enabled='1'";
$result = mysqli_query($connection, $query) or die(mysqli_error($connection));
if($row = mysqli_fetch_assoc($result)) { $dbpassword = $row['password']; }
if(password_verify($password, $dbpassword)) {
echo "Successful login";
}else{
echo "Invalid Login Credentials.";
}
I always get Invalid Login Credentials.
When I modify the new password for the user, I am doing the following:
$pass = mysqli_real_escape_string($connection, $_POST['password']);
$options = [ 'cost' => 10,
'salt' => mcrypt_create_iv(22, MCRYPT_DEV_URANDOM),
];
$password = password_hash($pass, PASSWORD_BCRYPT, $options)."\n";
$query = "UPDATE users
SET `password` = '".$password."'
WHERE id = ".$_POST['user_id']."
";
$result = mysqli_query($connection, $query) or die(mysqli_error($connection));
password in database is VARCHAR(255), and it is storing something like:
$2y$10$Y5HIyAsLMfkXIFSJONPsfO3Gxx3b46H.8/WFdLVH3Fqk2XNfy2Uaq
What am I doing wrong here?
The \n in the following line, is embedding a linebreak, (Edit: one that cannot be included in the user inputted password).
$password = password_hash($pass, PASSWORD_BCRYPT, $options)."\n";
and you need to delete it and start over with a new hash.
Jay Blanchard, a member here on Stack submitted a note about it not too long also in the password_hash() manual, which is something that he and I actually talked about.
Be care when using the example from the documentation which concatenates a newline character \n to the end of the hash, i.e.:
echo password_hash("rasmuslerdorf", PASSWORD_DEFAULT)."\n";
People are storing the hash with the concatenated newline and consequently password_verify() will fail.
Another option would be to use trim(); that also works (at the moment of hashing).
$password = password_hash($pass, PASSWORD_BCRYPT, $options)."\n";
$password = trim($password);
// Store in db after
Yet you still need to start over by clearing the old hash(es) and creating new ones.
Do keep in mind though, that you shouldn't escape passwords.
One such as 123'\abc (being perfectly valid) will be modified to 123\'\abc by real_escape_string(); it's not needed. password_verify() takes care of that, security-wise.
I think i have hashed password using function PASSWORD directly from mysql database(am i doing wrong here?). And i am trying to verify that password with this code:
if($submit)
{
$first=$_POST['first'];
$password=$_POST['password'];
$hash="*85955899FF0A8CDC2CC36745267ABA38EAD1D28"; //this is the hashed password i got by using function PASSWORD in database
$password=password_verify($password,$hash);
$db = new mysqli("localhost", "root","","learndb");
$sql = "select * from admin where username = '" . $first . "' and password = '". $password . "'";
$result = $db->query($sql);
$result=mysqli_num_rows($result);
if($result>0)
{
session_start();
$_SESSION['logged_in'] = true;
session_regenerate_id(true);
header("Location:loginhome.php");
}
}
But the password is not matching. What am i missing here?
UPDATE:
After all the suggestions i have used password_hash from php code to store into database.
$db = new mysqli("localhost", "root","","learndb");
$password=password_hash('ChRisJoRdAn123',PASSWORD_DEFAULT);
$sql="INSERT INTO admin (username,password)values('ChrisJordan','$password')";
$db->query($sql);
still the password is not matching.
One cannot search for a salted password hash in a database. To calculate the hash you need the password_hash() function as you already did correctly in your insert statement.
// Hash a new password for storing in the database.
// The function automatically generates a cryptographically safe salt.
$hashToStoreInDb = password_hash($password, PASSWORD_DEFAULT);
To check a password, you first need to search by username only (used a prepared query to avoid sql injection):
$sql = 'select * from admin where username = ?';
$db->prepare($sql);
$db->bind_param('s', $first);
When you finally got the stored hash from the database, it can be checked like this:
// Check if the hash of the entered login password, matches the stored hash.
// The salt and the cost factor will be extracted from $existingHashFromDb.
$isPasswordCorrect = password_verify($password, $existingHashFromDb);
password_verify is a boolean function which return either true or false. In your code, after getting value of password from Post param, you doing this operation
$password=password_verify($password,$hash);
which changes the $password value to true or false and that boolean value stored in $password you are using in mysql select statement
$sql = "select * from admin where username = '" . $first . "' and password = '". $password . "'";
Another thing is it might be possible that the hashed/salted password you are using is not the correct hashed value of the password you are using.
Update: Try this
$cost = [
'cost' => 15,
];
$hash_password = password_hash('ChRisJoRdAn123', PASSWORD_BCRYPT, $cost);
before any db operation, change your password field varchar length to >=64
$sql = "INSERT INTO admin (username,password)values('ChrisJordan','".$hash_password."')";
After insert operation, execute the select statement with the user
$sql = "select * from admin where username = 'ChrisJordan'";
after this fetching hased password and password from the post parameter, you will need to verify both passwords using password_verify
if (password_verify(validate($_POST['password']), $hash_password_from_db)) {
echo "Valid Password";
}else{
echo "Invalid Password";
}
You must use password_hash to encode passwords verified with password_verify.
The MySQL function PASSWORD is something entirely different. It is used for encoding passwords specific to MySQL authentication. (MySQL specifically recommends against using PASSWORD for anything other than MySQL authentication.)
The two use different hashing algorithms, present their output in different formats, and are generally not compatible with each other.
The typical way to use password_hash and password_verify is:
$hash = password_hash($password, PASSWORD_DEFAULT);
//Store $hash in your database as the user's password
//To verify:
//Retrieve $hash from the database, given a username
$valid = password_validate($password, $hash);
The problem in your code is that you're doing this:
$password=password_verify($password,$hash);
$sql = "select * from admin where username = '" . $first . "' and password = '". $password . "'";
password_verify returns a boolean (whether the password and hash matched). Instead, you need to retrieve the hash from the database and match the entered password with that hash.
This is too long for a comment.
Seeing that this question has yet to contain a green tick next to any of the answers, am submitting the following in order to point out probable issues.
I noticed that you are trying to move over from MD5 to password_hash() - password_verify().
Your other question Switching from md5 to password_hash
What you need to know is that MD5 produces a 32 character length string, as opposed to password_hash() being a 60 length.
Use varchar(255).
If you kept your password column's length to 32, then you will need to clear out your existing hashes from that column, then ALTER your column to be 60, or 255 as the manual suggests you do.
You will need to clear out all your existing passwords, ALTER your column, create a new hash, then try your login code again.
I see this in your code:
"*85955899FF0A8CDC2CC36745267ABA38EAD1D28"; //this is the hashed password i got by using function PASSWORD in database
This string *85955899FF0A8CDC2CC36745267ABA38EAD1D28 is 40 long, which is too short and has been cut off.
This tells me that your column's length is 40, instead of 60, or again as the manual suggests, 255.
MD5 reference:
http://php.net/manual/en/function.md5.php
Returns the hash as a 32-character hexadecimal number.
Reference for password_hash():
http://php.net/manual/en/function.password-hash.php
The result will always be a 60 character string, or FALSE on failure.
To ALTER your column, here is a reference link:
http://dev.mysql.com/doc/refman/5.7/en/alter-table.html
Also make sure that your form contains a POST method and that the inputs bear the matching name attributes and that no whitespace gets introduced.
You can use trim() to get rid of those.
Add error reporting to the top of your file(s) which will help find errors.
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
// Then the rest of your code
Sidenote: Displaying errors should only be done in staging, and never production.
as well as or die(mysqli_error($db)) to mysqli_query().
Edit:
What you need to do is fetch an array and get the match on that.
$sql = "select * from admin where username = '".$first."' and password = '".$password."' ";
$result = $db->query($sql);
if ($result->num_rows === 1) {
$row = $result->fetch_array(MYSQLI_ASSOC);
if (password_verify($password, $row['password'])) {
//Password matches, so create the session
// $_SESSION['user']['user_id'] = $row['user_id'];
// header("Location:/members");
echo "Match";
}else{
echo "The username or password do not match";
}
}
Another possible solution:
$query = "SELECT * from admin WHERE username='$first'";
$result = $db->query($query);
if($result->num_rows ===1){
$row = $result->fetch_array(MYSQLI_ASSOC);
if (password_verify($password, $row['password'])){
echo "match";
} else {
$error = "email or Password is invalid";
echo $error;
}
}
mysqli_close($db); // Closing Connection
I hope I formatted the code properly. I am having trouble making this if statement to work. I've searched and from what it looks like this statement should work. However, when I run it no matter the password if the username starts with kacey then it goes to echo "Logged in as: " . kacey;
Likewise, if I put the input to kaceyfeewaf, it still goes to echo "Logged in as: " . $myuser; This happens regardless of the password I put in. the line $result['username'] should validate to KACEY.
$sql = "SELECT * FROM $dbTable WHERE username = $myuser AND password = $mypass";
$result = mysql_query($sql);
if($result['username'] = $myuser && $result['password'] = $mypass;)
{
echo "Logged in as: " . $myuser;
} else {
echo "Fail ";
}
There are a few issues here.
Firstly, the variables you have in your query are strings, therefore they require to be quoted:
WHERE username = '$myuser' AND password = '$mypass'
Having or die(mysql_error()) to mysql_query() would have signaled the syntax error.
Then you're assigning instead of comparing with
if($result['username'] = $myuser && $result['password'] = $mypass;)
use two equals ==
However, that isn't how you check if those rows exist.
You need to use mysql_num_rows() or use a while loop while using a function to fetch/iterate over results found.
Here is an MySQLi example using mysqli_num_rows():
$conn=mysqli_connect("hostname","username","password","db");
$check_select = mysqli_query($conn, "SELECT * FROM `users`
WHERE email = '$email' AND pw='$pass'");
$numrows=mysqli_num_rows($check_select);
if($numrows > 0){
// do something
}
Now, we don't know where those variables have been assigned, and if from a form that it's using a POST method with matching name attributes.
I.e.:
<form action="" method="post">
<input type="text" name="username">
...
</form>
$username = $_POST['username'];
Another thing which is unknown to us is the MySQL API you're using to connect with. Make sure that you are indeed using the same one as you are using to query with, being mysql_. Different APIs do not intermix, such as mysqli_ or PDO. Use the same one from connection to querying.
Add error reporting to the top of your file(s) which will help find errors.
<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);
// rest of your code
Sidenote: Displaying errors should only be done in staging, and never production.
I noticed you may be storing passwords in plain text. If this is the case, it is highly discouraged.
I recommend you use CRYPT_BLOWFISH or PHP 5.5's password_hash() function. For PHP < 5.5 use the password_hash() compatibility pack.
Here is a PDO solution pulled from one of ircmaxell's answers:
https://stackoverflow.com/a/29778421/
Just use a library. Seriously. They exist for a reason.
PHP 5.5+: use password_hash()
PHP 5.3.7+: use password-compat (a compatibility pack for above)
All others: use phpass
Don't do it yourself. If you're creating your own salt, YOU'RE DOING IT WRONG. You should be using a library that handles that for you.
$dbh = new PDO(...);
$username = $_POST["username"];
$email = $_POST["email"];
$password = $_POST["password"];
$hash = password_hash($password, PASSWORD_DEFAULT);
$stmt = $dbh->prepare("insert into users set username=?, email=?, password=?");
$stmt->execute([$username, $email, $hash]);
And on login:
$sql = "SELECT * FROM users WHERE username = ?";
$stmt = $dbh->prepare($sql);
$result = $stmt->execute([$_POST['username']]);
$users = $result->fetchAll();
if (isset($users[0]) {
if (password_verify($_POST['password'], $users[0]->password) {
// valid login
} else {
// invalid password
}
} else {
// invalid username
}
You should use == instead of simple = for your if condition
First of all delete that if stmt and make new one where you check for num rows. If there is num rows > 0 you have valid login. And then print needed results from database od current query.
Edit:
You have = insted of == or === so stmt is always true.
I can't get any result from a function attempt_login() that I made myself. Can anyone figure out any errors or what is wrong with this piece of code? It worked for only one user that I created in the database. I want to get a boolean in return and afterwards wanna check if the result is true, than store it into the session and then redirect the user to a specific page. Please help!
CODE:
function attempt_login($theusername, $thepassword){
$host = 'localhost';
$username = 'root';
$password = '';
$database = 'website';
$con = mysqli_connect($host, $username, $password, $database);
$theusername = mysqli_real_escape_string($con, $theusername);
$thepassword = mysqli_real_escape_string($con, $thepassword);
$thepassword = md5($thepassword);
$query = "SELECT * FROM members WHERE username = '{$theusername}' AND password = '{$thepassword}'";
$result = mysqli_query($con, $query);
$row = mysqli_fetch_array($result);
return isset($row['name']);
mysqli_close($con);
}
SCREEN SHOT:
Let's say your password is abc'123 - you know, a good mix of letters, numbers and symbols.
mysqli_real_escape_string will transform that into abc\'123.
md5 then makes it af94b3b7388e50429710ed345b58c01c
But in the database, the password abc'123 should be encrypted as bb7e1fc94ff2a3e8d2d79c8ab055da60, which is significantly different!
Try moving the md5 call before the mysqli_real_escape_string.
EDIT: Your first user works because the password is fishman001
The second, however, is the empty string. You are probably using if( empty($_POST['password'])) or similar to see if a password was submitted. The empty string will fail this test.
PS: Rainbow tables. Love 'em. Now stop using MD5.