How to retrieve password from database with password_verify()? - php

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

Related

Pulling a hashed username from MySQL database

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.

Name and password individual check in PHP

I am writing a deliberately vulnerable web application. I'd like to figure out how to check username and password, matching against the database and each other as well.
So: if the username exists in the database and the password exists in the database and the username and password belongs together. I'm fully aware how to send a query which checks for both at the same time and returns either true or false, so please don't start on that. My goal is to individually check for both so I can inform the user which one is not working.
Here's my code but as I'm not really a PHP person this is obviously not working:
<?php
if(isset($_POST["submit"])){
$username = mysqli_real_escape_string($conn, $_POST['username']);
$password = mysqli_real_escape_string($conn, $_POST['password']);
$sql_username = "SELECT id FROM users WHERE username = '$username'";
$sql_password = "SELECT id FROM users WHERE password = '$password'";
$result_username = mysqli_query($conn, $sql_username);
$result_password = mysqli_query($conn, $sql_password);
$row_username = mysqli_fetch_array($result_username);
$row_password = mysqli_fetch_array($result_password);
$count_username = mysqli_num_rows($result_username);
$count_password = mysqli_num_rows($result_password);
if($count_username > 0 && $count_password < 0) {
echo "Invalied password";
} else if ($count_username < 0 && $count_password > 0) {
echo "Invalied username";
} else {
"Welcome";
}
}
?>
Any hints?
Edit
$conn can be used as I'm getting it from another php file.
<?php
//set up a connection and all that prerequisite stuff
$sqlConnectionNameHere = new mysqli($sql_host, $sql_username, $sql_password, $sql_dbname);
$username = 'bob'; //the username that will be checked
$password = 'securepassword1'; //the password that will be checked
$query = $sqlConnectionNameHere->query("SELECT username, password FROM users WHERE username='$username' LIMIT 1"); //make your query
if ($query->num_rows != 1){ //if the datbase didn't return any rows, the user with $username must not exist
die('User not found!');
}
while ($user = $query->fetch_assoc()){
if ($user['password'] != $password){
die('Invalid Password');
}
}
//if they've made it here, the user exists and the password matches!
echo 'Welcome!';
?>
This is a really barebones way of doing it. You may want to add more security to this.
Hope this helps. Best of luck to you!
I know its way cooler to let the user know wether the username or password is wrong. But this actually helps get unwanted indivuduals to pass this test. Since they can test if a specific user is present or not. From there this individual would bruteforce the password. So its a good practice to just check for username AND password.
Furthermore you can store the password as a hash. This way not even the hoster of your db has a direct access to the passwords. Use MD5 or SHA. As example the credentials in your db would look like:
User Password
Admin 098f6bcd4621d373cade4e832627b4f6
This is a MD5 hash. You'll get this hash if you process md5('test'). A hash is a one way "encryption" function. You check it by hashing the user-input and comparing this result with the stored hash.
If you check for this password you'd bind it like this:
$stmt->bind_param("ss", $_POST['username'], md5($_POST['password']));
$mysqli = new mysqli("localhost", "my_user", "my_password", "my_db");
Plaintext password(not recommended!) - prepared select
$stmt = $mysqli->prepare("SELECT id FROM users WHERE username=? AND password=?");
// type string, string ("ss")
$stmt->bind_param("ss", $_POST['username'], $_POST['password']);
// hashed (recommended)
// $stmt->bind_param("ss", $_POST['username'], md5($_POST['password']));
$stmt->execute();
// set target for the id-column
$stmt->bind_result($id);
// Will be TRUE if a row has been found
if( $stmt->fetch() ) {
echo "Welcome! user:{$id}";
} else {
echo "Invalid username or password";
}
For details have a look at mysqli prepare
And a little more security related: Password hashing

I've got a mysql/php hash(sha1) login issue

I have a issue regarding logging in with password being hashed in database.
My current script just tells me 'bad password' message even when its correct.
$s = $mysqli->query("SELECT * FROM `users` WHERE email`='".$_POST['email']."'") or die();
$i = $s->fetch_assoc();
if($_POST['password'] == sha1($i['password'])) {
echo "works";
} else {
echo "bad password";
}
You are doing it the wrong way round. The database password is already hashed I hope but the user enters a plain text password, so you need to hash what the user enters and see if that matches the hash you have on the database
$s = $mysqli->query("SELECT * FROM `users` WHERE `email`='{$_POST['email']}'") or die();
$i = $s->fetch_assoc();
if(sha1($_POST['password']) == $i['password']) {
echo "works";
} else {
echo "bad password";
}
However
Please dont roll your own password hashing. PHP provides password_hash()
and password_verify() please use them, I might want to use your site one day
And here are some good ideas about passwords
If you are using a PHP version prior to 5.5 there is a compatibility pack available here
Also
Your script is at risk of SQL Injection Attack
Have a look at what happened to Little Bobby Tables Even
if you are escaping inputs, its not safe!
Use prepared statement and parameterized statements
Here is an example of how you can verify sha with mysql safely.
<?php
// Basic php MYSQL authentication with crypted password
$username = $_POST['username'];
$password = $_POST['password'];
$salt = "CrazyassLongSALTThatMakesYourUsersPasswordVeryLong123!!312567__asdSdas";
$password = hash('sha256', $salt.$password);
//echo $password;
// Mysql connection
$mysqli = new mysqli("localhost","mysqluser","mysqlpassword","mysqldatabase");
$stmt = $mysqli->prepare('SELECT userid FROM Users WHERE password = ? AND username = ?');
// (ss -> string, string) Always bind parameters and use prepared statement to improve security
$stmt->bind_param("ss", $password, $username);
$stmt->execute();
$stmt->bind_result($userid );
if (!empty($stmt->fetch())) {
// if fetch is not empty we have results and password hash was correct
echo "User was found";
} else
echo "User was not found";
$mysqli->close();
?>

Password is not verified using function password_verify

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

Logging in issues with salt and hash

On my registration page I have used an SHA1 has and a salt to store my passwords in the database. I think I have done this correctly as when I check the database it is has with the salt included. This is how I have done it.
$newPassword = $_POST['Password'] ;
if (!empty($newPassword)) {
//Escape bad characters
//$newuser = mysql_real_escape_string($newuser);
//remove leading and trailing whitespace
$newPassword = trim($newPassword);
$newPassword = sha1($newPassword);
$salt = '-45dfeHK/__yu349#-/klF21-1_\/4JkUP/4';
}
else die ("ERROR: Enter a Password");
and input is
$query = "INSERT INTO members (memberFirstname, memberSecondname, memberEmailaddress, memberPassword, memberAddress, memberPostcode) VALUES ('$newFirstName', '$newSecondName', '$newEmailAddress', '$newPassword$salt', '$newAddress', '$newPostcode')";
My problem lays when I try to login. Im unsure on how remove the salt and unhash the password (if that is what needs to be done). I can enter the email address and paste the hash and salt into the password field and can successfully login.
This is my script to log in.
<?php
include 'db.inc';
session_start();
$UserEmail =$_POST["EmailAddress"];
$UserPassword =$_POST["Password"];
$query = "SELECT * FROM members WHERE memberEmailaddress = '$UserEmail'
AND memberPassword = '$UserPassword' ";
$connection = mysql_connect($hostname, $username, $password) or die ("Unable to connect!");
mysql_select_db($databaseName) or die ("Unable to select database!");
$result = mysql_query($query) or die ("Error in query: $query. ".mysql_error());
// see if any rows were returned
if (mysql_num_rows($result) > 0) {
$_SESSION["authenticatedUser"] = $UserEmail;
// Relocate to the logged-in page
header("Location: Index.php");
}
else
{
$_SESSION["message"] = "Could not connect log in as $UserEmail " ;
header("Location: Login.php");
}
mysql_free_result($result);
mysql_close($connection);
?>
There are several problems with your approach. First you don't use the salt at all, it will be stored but not used. Second a salt should be unique for each password, in your case a static salt is used, this is actually a pepper not a salt. Further you use a fast hash algorithm, but this can be brute-forced ways too fast, instead you should switch to a hash algorithm with a cost factor like BCrypt or PBKDF2.
PHP already has a good function to hash passwords (maybe you need the compatibility pack):
// Hash a new password for storing in the database.
// The function automatically generates a cryptographically safe salt.
$hashToStoreInDb = password_hash($password, PASSWORD_BCRYPT);
// 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);
Because this function generates a safe salt on its own and attaches it to the resulting hash-value, you cannot check the password with SQL directly. Instead you do a query to get the stored hash (by username), then you can verify the entered password with the stored one. I wrote a tutorial where i tried to explain the important points more indepth.

Categories