Pulling a hashed username from MySQL database - php

I'm working on a project where both the username and password need to be hashed with Argon2. I'm not having any trouble hashing them both in the registration and inserting them into the database, but I'm unable to pull the information for the login. Here is my login script:
<?php session_start(); ?>
<?php
include 'config.php';
if(isset($_POST['submit'])){
$submittedUser = $_POST['username'];
$submittedPass = $_POST['password'];
$encrypteduser = password_hash($submittedUser, PASSWORD_ARGON2I);
$con=mysqli_connect($servername, $dbusername, $dbpassword, $dbname);
// Check connection
if (mysqli_connect_errno())
{
echo "Failed to connect to MySQL: " . mysqli_connect_error();
}
if ($stmt = mysqli_prepare($con, "SELECT * FROM users Where username =?")) {
mysqli_stmt_bind_param($stmt, "s", $encrypteduser);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);
}
while($row = mysqli_fetch_array($result))
{
$username = $row['username'];
$password = $row['password'];
}
if (password_verify($submittedUser, $username) && password_verify($submittedPass, $password))
{
$_SESSION['user']=$username;
echo "<script> location.href='index.php'; </script>";
exit;
}
else
{
echo "<script> location.href='login.php'; </script>";
exit;
}
mysqli_close($con);
}
?>
My current theory is that the hash being generated and stored in $encrypteduser does not match the one in the database. That would explain why no result is being pulled. Is there a way to get around this?

This does not encrypt, it hashes:
$encrypteduser = password_hash($submittedUser, PASSWORD_ARGON2I);
I.e., it's one way. You (theoretically) can never get the original text back from it. It will also generate a different result every time you run it. As a result, you'll never be able to run a query with a WHERE clause to pick out one matching user. Rather, you'd have to iterate over every user in the system, running password_verify() against each of them.
So... don't do that. Leave the username in plain text so that you can benefit from the database index.

What you want to do cannot be done because having the username stored in clear is exactly what allows to you determine what exact credentials (i.e. table row) you need to validate against.
Imagine you tried anyway. You want to validate john.doe. You would have to loop on every stored username hash, grab the corresponding salt so you can calculate the hash with john.doe and current row salt and then compare both username hashes. If there's no match, go to next row... until you eventually get a match and can finally determine what password hash to check. All this, with al algorithm specifically designed to be slow. Go figure.

Related

I need to check if the password is correct but it is hashed in the database

I need to create a login system for an application that will work off the site and I need it to connect to the standard WordPress database but the passwords in the database are protected by Hash.
I would like to know if you can cancel the Hash encoding or if there is any php script that can get the user's original password without being decoded by Hash.
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
echo "Connected successfully.<br>";
$sql = "SELECT user_pass FROM testeUsers WHERE user_email = '".$loginUser . "'";
$result = $conn->query($sql);
if ($result->num_rows > 0) {
// output data of each row
while($row = $result->fetch_assoc()) {
if($row["user_pass"]== $loginPass){
echo "Login Sucess";
//colocar as funções aqui
}
else{
echo "wrong password";
}
}
} else {
echo "User not found";
}
The problem is that this PHP code takes the text that is written in the user_pass field in the database and that user_pass is not the user's real password because it is in Hash, so my user will never be able to log into the system even his password being correct.
No, the entire point of the hash is to be a one-way function - i.e. you can't easily reverse it. If you could easily reverse it, presumably hackers could also easily reverse it and the hash would be pointless.
In order to compare the passwords, you need to hash the password that the user entered with the salt used in the database, and then do the comparison. I believe that there is a standard function to do this in PHP, but I truthfully don't recall its exact name.

How to retrieve password from database with password_verify()?

I'm learning PHP and as a project I started building a social network. I did create the signup form and login form and I can add users to my database. I also hash their passwords. This is a simple site and a work in progress so there are a lot of security holes.
My problem is with the login file, I can't seem to match the user with the password he has given me. For verifying the user password I use the password_verify() function but it doesn't seem to be working right.
Here is my code:
Sign up
<?php
//signUp.php
//Here is where I add a user in my database
//I validate the input, confirm that the password is written like it should be
//check if a user with the same username exists in the database
//if all checks out I will add the user in the database
//and redirect the user to his profile
require_once 'login.php';
require_once 'helperFunctions.php';
$conn = new mysqli($servername, $username, $password, $database);
if(!$conn)
die("Connection failed:" . mysqli_connect_error());
$myUsername = $_POST['Name'];
$myPassword = $_POST['Password'];
$myConfirm = $_POST['conPass'];
sanitize($conn, $myUsername);
sanitize($conn, $myPassword);
//check if the two passwords are the same
if($myPassword != $myConfirm){
print "Your passwords don't match";
header("refresh: 5; index.html");
} else {
//check if username already exists in database
$query = "SELECT * FROM members WHERE Username='$myUsername'";
$result = mysqli_query($conn, $query);
$count = mysqli_num_rows($result);
if($count == 0){
//hash password
$hashedPass = password_hash("$myPassword", PASSWORD_DEFAULT);
//username doesn't exist in database
//add user with the hashed password
$query ="INSERT INTO members (Username, Password) VALUES ('{$myUsername}', '{$hashedPass}')";
$result = mysqli_query($conn, $query);
if(!$result)
die("Invalid query: " . mysqli_error());
else{
print "You are now a member or The Social Network";
header("refresh: 5; login_success.php");
}
} else {
print "Username already exists";
header("refresh: 5; index.html");
}
}
?>
Login
<?php
//checkLogin.php
//Here is where I authenticate my users and if successfull I will show them their profile
require_once 'login.php';
require_once 'helperFunctions.php';
$conn = new mysqli($servername, $username, $password, $database);
if(!$conn)
die("Connection failed:" . mysqli_connect_error());
//Values from form
$myUsername = $_POST['Name'];
$myPassword = $_POST['Password'];
//sanitize input
sanitize($conn, $myUsername);
sanitize($conn, $myPassword);
$query = "SELECT * FROM members WHERE Username='$myUsername'";
$result = mysqli_query($conn, $query);
$count = mysqli_num_rows($result);
if($count == 1){
$row = mysqli_fetch_array($result, MYSQLI_ASSOC);
print "hashedPass = ${row['Password']}";
print "myPassword: " . $myPassword;
if(password_verify($myPassword, $row['Password'])){
print "Password match";
} else
print "The username or password do not match";
}
?>
Sanitize function
function sanitize($conn, $val){
$val = stripslashes($val);
$val = mysqli_real_escape_string($conn, $val);
}
By running the program print "hashedPass = ${row['Password']}"; prints out the hashed password which is the same with the one I have on my database but for some reason I get redirected to the print "The username or password do not match"; statement after this.
Comment pulled and taken from a deleted answer:
"I remembered that when I first created the database I used CHAR(10) for the passwords while the hashed password needs more characters."
so the almighty answer here is that your password column is 50 characters short.
password_hash() creates a 60 characters string.
the manual states that it is best to use VARCHAR and with a length of 255 in order to accommodate for future changes.
http://php.net/manual/en/function.password-hash.php
the solution to this now, is to start over with a new registration and then login again using what you are presently using.
Example from the manual:
<?php
/**
* We just want to hash our password using the current DEFAULT algorithm.
* This is presently BCRYPT, and will produce a 60 character result.
*
* Beware that DEFAULT may change over time, so you would want to prepare
* By allowing your storage to expand past 60 characters (255 would be good)
*/
echo password_hash("rasmuslerdorf", PASSWORD_DEFAULT)."\n";
?>
The above example will output something similar to:
$2y$10$.vGA1O9wmRjrwAVXD98HNOgsNpDczlqm3Jq7KnEd1rVAGv3Fykk1a
Also from the manual:
Caution
Using the PASSWORD_BCRYPT for the algo parameter, will result in the password parameter being truncated to a maximum length of 72 characters.
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).
PASSWORD_BCRYPT - Use the CRYPT_BLOWFISH algorithm to create the hash. This will produce a standard crypt() compatible hash using the "$2y$" identifier. The result will always be a 60 character string, or FALSE on failure.
Supported Options:
Another comment/question pulled from the deleted answer:
"Can I alter my password field without having to delete my table and start from the beginning?"
The answer is yes. See this Q&A on Stack:
How can I modify the size of column in a mysql table?
You can also consult:
https://dev.mysql.com/doc/refman/5.0/en/alter-table.html
Sidenote: You will still need re-enter new hashes for the (old) affected column(s).
Plus, as already stated; you are open to SQL injection. Use a prepared statement:
https://en.wikipedia.org/wiki/Prepared_statement

can't pull info form database that uses PASSWORD() function

I have this user login process page. at this point the user has entered the info and all of this works BUT I cannot figure out how to pull the encrypted password out of the DB. I need to extract with the PASSWORD() function and do not know how. I know this is not the best way to do it but its what the assignment calls for. I have the problem section commented out I think thats what needs fixing.
//sets $query to read usnername and passowd from table
$query = "SELECT username,password,first_name,last_name FROM jubreyLogin WHERE username
= '$userName' AND password=password('$userPassword')";
$result = mysql_query($query,$db);
if(mysql_error())
{
echo $query;
echo mysql_error();
}
//reads data from table sets as an array
//checks to see if user is already registered
while($row=mysql_fetch_array($result))
{
if($userName == $row['username'] /*&& $userPassword == ($row['password'])*/)
{
$login = 'Y';
$welcome = "Welcome" . " " .$row['first_name']. " " .$row['last_name'];
$userName = $row['username'];
}
}
if ($login='Y')
{
setcookie('name',$welcome,time()+60*60*24*30);
setcookie('login',"Y",time()+60*60*24*30);
$_SESSION['username_login'] = $userName;
header('Location: welcome.php');
}
Here is the modified code that I should of posted first I need it to check user entered password in this case $userPassword with the encrypted password if its a match it will send the user into the next page of the site.
You don't need to see the password in clear text ( you can't even if you wanted to). As you are checking the record both on password and username you don't need the check in your if() statement. If there is any row found, that means the username/password combination was succesfful and the user can be deemed as logged in.
Edit:
The updated code doesn't really make any difference to the actual logic. The logic stays the same, you query the database with username AND encrypted password, if there is a match that means the user has the right to login, so you proceed with setting the cookies/session data and redirect. Although I do not really see the need for the login cookie and the welcome cookie cause you could simply put in both username, fname and lname in the session. If the session on the following pages contains username that means the user has logged in.
The code can go something like this:
//sets $query to read usnername and passowd from table
$query = "SELECT username,first_name,last_name FROM jubreyLogin WHERE username = '$userName' AND password=password('$userPassword')";
$result = mysql_query($query,$db);
if(mysql_error())
{
echo $query;
echo mysql_error();
}
// were any rows returned?
if(mysql_num_rows($result)){
list($userName, $firstName , $lastName) = mysql_fetch_row($result);
$welcome = "Welcome" . " " .$firstName. " " .$lastName;
setcookie('name',$welcome,time()+60*60*24*30);
setcookie('login',"Y",time()+60*60*24*30);
$_SESSION['username_login'] = $userName;
header('Location: welcome.php');
}
You should not be encrypting your passwords, you should be hashing them. Try using a library such as phpass to be on the safe side. What you will need to do is hash the passwords and store the hashed value in the database. When a user logs in, you will hash the password they provide and compare that with the hashed value in the database. If the hashes match, the password provided is correct. If not, you send an error to the user. You never need to be able to obtain the password in plain text in order to validate a user.
Also, make sure that you are either escaping your variables using mysql_real_escape_string() or prepared statements or your script will be vulnerable to SQL injection.

Password look up

Need a bit of help with a form. I have created a form which require log in. Once a person has logged in they complete the form and then someone else checks the form and enters there password before the form is submitted.
I have set up some rules which checks the fields are completed correctly and I want to write some code that will check the password field is completed and then check it against the stored passwords in the database.
So far I have got this.
if (!empty($_POST['password']))
{
/*connect to database to check password is valid*/
$user_name = "contains username for database";
$pass_word = "contains password";
$database = "name of database";
$server = "localhost";
$db_handle = mysql_connect($server, $user_name, $pass_word);
$db_found = mysql_select_db($database, $db_handle);
if ($db_found) {
$uname = quote_smart($uname, $db_handle);
$pword = quote_smart($pword, $db_handle);
$SQL = "SELECT * FROM masterpass WHERE password = $password";
$result = mysql_query($SQL);
$num_rows = mysql_num_rows($result);
if ($result) {
if ($num_rows > 0) {
continue;
}
else {
$error = true;
}
Not sure if I am going about this the right way so any help would be great.
Thanks in advance
Matt
for starters, first you create $pword:
$pword = quote_smart($pword, $db_handle);
and in your query you use $password.
$SQL = "SELECT * FROM masterpass WHERE password = $password";
This can't work.
Secondly, you should ask for username AND password in your query.
Last but not least: never save a password in clear text in your database. Generate a MD5 hash!
I have set up some rules which checks the fields are completed correctly and I want to write some code that will check the password field is completed and then check it against the stored passwords in the database.
No, you don't. Checking to see if the password is already in the database is not a very smart thing to do, as that opens your application to brute-forcing attacks. I could use your form to determine which passwords are used, and if I can get a list of your users, I can try each of those passwords to each of those users and get access.
Secondly, quote_smart is probably not smarter than mysql_real_escape_string. Use that instead.
Thirdly, as Sascha already mentions, please generate a hash. I wouldn't use MD5, but sha1 instead, but even using MD5 without salt already increases the security in your form dramatically.
My mantra on validating passwords is: make sure it's longer than 7 characters, that's it. Don't make assumptions on what password people should use. I hate it if I type in a password and some validation routine tells me I can't use {^ in my password.

Need help making login page with salts

Alright, I'm trying to make a login page. It seems that all of the pages worked pretty good- until I added salts. I don't really understand them, but doing something as basic as I am shouldn't be to hard to figure out. Here's "loginusr.php":
<html>
<body>
<?php
//form action = index.php
session_start();
include("mainmenu.php");
$usrname = mysql_real_escape_string($_POST['usrname']);
$pass = $_POST['password'];
$salt = $pass;
$password = sha1($salt.$pass);
$con = mysql_connect("localhost", "root", "g00dfor#boy");
if(!$con)
{
die("Unable to establish connection with host. We apologize for any inconvienience.");
}
mysql_select_db("users", $con) or die("Can't connect to database.");
$select = "SELECT * FROM data WHERE usrname='$usrname' and password='$password'";
$query = mysql_query($select);
$verify = mysql_num_rows($query);
if($verify==1)
{
$_SESSION["valid_user"] = $usrname;
header("location:index.php");
}
else
{
echo "Wrong username or password. Please check that CAPS LOCK is off.";
echo "<br/>";
echo "Back to login";
}
mysql_close($con);
?>
</body>
</html>
I used the command echo $password; to show me if the password in the database matched with the script. They did. What am I doing wrong?
It seems like you've misunderstood salts, since you're setting $salt to be the password.
A salt should be a completely random string that's stored in a user record along with the password hash. A new unique salt should be generated for every user. So you need to add a new column to your database, called "password_salt" or similar.
Rather than trying to use the password in the SELECT query and see if you get any records, you actually need to just SELECT using the username/user_id in order to get the password hash and salt so that you can then use those to determine if the user entered the correct password.
When you sign up new users you should add the fields with values like this,
<?php
// This is registeruser.php
$salt = substr(sha1(uniqid(rand(), true)), 0, 20);
$pass = $_POST['password'];
$pass_to_store = hash("sha256", $salt.$pass);
// Then issue a DB query to store the $salt and $pass_to_store in the user record.
// Do not store $pass, you don't need it.
// e.g. INSERT INTO users ('username', 'password_salt', 'password_hash') VALUES (:username, :salt, :pass_to_store);
?>
Then to check the password is the same when logging in, you do something like this,
<?php
// This is loginuser.php
$user = // result from SQL query to retrieve user record
// e.g. SELECT password_hash, password_salt FROM users WHERE username='from_user'
$salt_from_db = $user['password_salt'];
$pass_from_db = $user['password_hash'];
if ($pass_from_db == hash("sha256", $salt_from_db.$_POST['password'])
{
// Password matches!
}
?>
Don't forget to sanitize user inputs and anything you're putting into your database. You might want to look into using prepared statements instead of having to remember to use mysql_real_escape_string all the time.
It looks like you're salting with the same password? Normally a salt would be a random key that is specific to your site that you prepend to the password input, which it looks like you're doing fine. Just make sure you're using that same salt for checking that you use when the password is created.
Also, to use sessions properly you need to have session_start before anything is output to the page:
<?php
session_start();
?>
<html>
<body>
...
A salt is a random value to prevent an attacker from just looking up the source of a hash in table generated based on common passwords. (Using the username as salt is obviously not a good idea as it only adds very little entropy).
So you need to store the salt in the database and read it from the database in order to calculate the salted password hash for comparison with the stored value.
You misspelled username a couple of times, is it misspelled in the database, too?

Categories