I've got a login script that I'm just starting on. When a new password is entered it is first encrypted using MD5, then stored in the database.
When I type the username and password in my login form and submit it, I'm trying to verify the stored password against a $_POST variable like this:
$username = $_POST['username'];
$password = md5($_POST['password']);
//database stuff here
$q = mysql_query("SELECT * FROM Users WHERE username='$username'");
while ($row = mysql_fetch_array($q))
{
if ($row['password'] == $password)
{
echo "Passwords match.";
}
else
{
echo "Password is incorrect.";
echo "<br />Entered password: " . $password;
echo "<br />Stored password: " . $row['password'];
}
}
This is just in the testing stages, so the password that I'm attempting to verify is 'password', for simplicity. If I output $_POST['password'], I get password - however, if I output the MD5 hash as stored in the database and md5($_POST['password']), they do not match. The latter has extra characters. Anyone know why this is happening?
Your column type could be truncating the data when you store it.
Check that it is at least 32 characters (the size of an MD5 hash).
Also, this script is vulnerable to SQL injection.
Your field is too short. MD5 hashes have 32 hexadecimal digits.
It is possible to get tricked. Check this:
$str=md5('password');
print(strlen($str));
and the output is 32. (the string is "5f4dcc3b5aa765d61d8327deb882cf99")
Now try inserting this within html code:
<?=print(md5('password'));?>
And the output is "5f4dcc3b5aa765d61d8327deb882cf991" (33 chars)
The issue gets clearer when you insert:
<?=print(strlen(md5('password')));?>
The output is "321"
This works:
<?php print(strlen(md5('password')));?>
Despite the other answers, MD5 as an algorithm does not produce hexadecimal characters at all. MD5 is an operation that is performed on binary data. As output, it returns 16 bytes of binary data.
It's the PHP function that returns a hexadecimal string. It depends on the way you want to handle the output of the hash if this is what you want. If you store the hash as binary data you might want to use the "raw" output:
string md5 ( string $str [, bool $raw_output = false ] )
Related
This question already has answers here:
How to use PHP's password_hash to hash and verify passwords
(5 answers)
Closed 1 year ago.
Here's my code:
function login() {
//Declare variables
$username = $_POST["login"];
$password = $_POST["password"];
$client = $_POST["clients"];
//QueryDB
$servername = "localhost";
$SQLUsername = "XXXX";
$SQLPassword = "XXXX";
$dbname = "XXXX";
$conn = new mysqli($servername, $SQLUsername, $SQLPassword, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
//Set query then run it
$sql = "SELECT * FROM Users WHERE Username ='$username'";
$result = $conn->query($sql);
$row = $result->fetch_array(MYSQLI_ASSOC);
//Verify Password
if ($result->num_rows === 1) {
if (password_verify($password, $row['Password'])) {
//Build XML request
$XMLRequest = "<?xml version='1.0'?><Login><Client>".$client."</Client><LoginDetails><Username>".$username."</Username><Password>".$newhash."</Password></LoginDetails></Login>";
//Build URL
$ReplacedValues = array("NewType" => "login", "NewHash" => $XMLRequest);
$NewString = strtr($GLOBALS["params"], $ReplacedValues);
$NewUrl = $GLOBALS["link"].$NewString;
//Post to Server
header('Location: '.$NewUrl);
}
else {
echo "Password is wrong"."<br>";
echo $password."<br>";
echo $row['Password'];
}
} else {
echo "more then 1 row";
}
mysqli_close($conn);
}
My issue is that even if I hard code my password variable and Hash variable to their respective values the if condition returns false. Any idea why? The page does when it loads, loads the else condition to show me the user input password and the correct hash value from the DB. My DB is set to CHAR(255) for the password.
UPDATE**
Here is my C# discussed in the comments. This is not the complete code just up to the part of the insert statement for the DB. I am able to insert into the SQL server DB just fine.
public static string WebsiteRegister(XmlDocument XMLBody)
{
//Get SQL connection string
XmlNodeList XMLNodes = SQLConnectionMethods.EstablishSQLServerConnection("SQL");
string ConnectionString = XMLNodes.Item(0).ChildNodes[0].InnerText;
string UserName = XMLNodes.Item(0).ChildNodes[1].InnerText;
string Password = XMLNodes.Item(0).ChildNodes[2].InnerText;
//Open connnection
SqlConnection cnn = new SqlConnection(ConnectionString);
SQLConnectionMethods.OpenSQLServerConnection(cnn);
try
{
string username = XMLBody.SelectSingleNode("register/registerdetails/username").InnerText;
string pass = XMLBody.SelectSingleNode("register/registerdetails/password").InnerText;
string fname = XMLBody.SelectSingleNode("register/registerdetails/firstname").InnerText;
string lname = XMLBody.SelectSingleNode("register/registerdetails/lastname").InnerText;
string email = XMLBody.SelectSingleNode("register/registerdetails/email").InnerText;
string accountRef = XMLBody.SelectSingleNode("register/registerdetails/accountreference").InnerText;
string client = XMLBody.SelectSingleNode("register/client").InnerText;
//Build Query string
string queryString = $"Insert into [dbo].[UserAccounts] (AccountReference, FirstName, LastName, Email, Username, Pass, Client) values ('{accountRef}', '{fname}', '{lname}', '{email}', '{username}', '{pass}', '{client}')";
//Process request
using (SqlCommand myCommand = new SqlCommand(queryString, cnn))
{
string Result = (string)myCommand.ExecuteScalar();
I could not find any problem in your code here but since hashing and verifying is a process which depends on a lot of factors to be successful i would like to give you some tips so that you can check it yourself for any potential problems.
make sure you are not escaping/sanityzing the password before
hashing it. This way you're altering the stored password. Let
$password = $_POST['password'];
both when you create the account and
when you check if the password match at login.
make sure you are enclosing the hash variable in single quotes (') and not double quotes (").using double quotes makes PHP read each paired character with "$" as indivisual variables which will probably cause your code to break, USE SINGLE QUOTES INSTEAD.
Ensure the Password field in the database (that stores the hashed
password) is able to store up to 255 characters. From the documentation
it is recommended to store the result in a database column that can
expand beyond 60 characters (255 characters would be a good choice). If the field is narrower the hash will be truncated and you'll never
have a match.
As you get the user by username at login ensure that username is unique
as long as it is your primary key (in the table
definition). Good idea to check this also upon user registration and login (at php level).
echo both hash and entered
values and make sure they match the ones that were inserted and
generated during password_hash() if the database value was different
(the hash), make sure the type of its column is varchar(256), the
hash is usually 60 characters long but the hashing function is
frequently improved so that length may expand in the future.
if the entered value was different (the user password), make sure the
filtering isn't corrupting the password value.
also check if another variable has the same name as the one you're storing the password in.
If password_verify($password, password_hash($password, PASSWORD_DEFAULT))
"works", then the problem is that $row['Password'] does not contain what is expected - including not being generated correctly.
If it "doesn't work" then
another line is causing the observed behavior. Both of these
outcomes allow focusing on a refined problem set.
try changing the password from the another column that matched and copy it to your password column and enter that password to check.if it worked then there is problem with your stored password
try copying this hash to your password column
$2y$10$uDnEpQpkh9RO5tWwOrHtneLAuYMRDstRaKGZFyHubObDR0789OMO6
this is the hashed password for 123456 using bycrypt. select this password and try to verify it with password_verify(123456,$yourhashedfromdatabase) after selecting this from sql if it works then there is probably some manipulation after your data is entered and goes to the database. it it does not then check if the password that reaches the database is exactly as your typed by echoing it.
UPDATE: after you updated your code i see that while entering the password you use "Pass" column but while extracting you use "Password" column. this could be the issue
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 am using the following two functions to hash passwords on a registration form:
function password_encrypt($password){
$hash_format = "$2y$10$"; //2y means use blowfish. 10 is cost parameter - number of times to run the blowfish hash.
//Blowfish wants salts of 22 characters or more.
$salt_length = 22;
$salt = generate_salt($salt_length);
$format_and_salt = $hash_format . $salt;
$hash = crypt($password, $format_and_salt); ///If this was echoed, salt would appear at beginning as part of result.
//Means hash can be passed in again as a salt. Then the has contains the salt making it easy to compare.
return $hash;
}
function generate_salt($length){
//MD5 returns 32 characters
$unique_random_string = md5(uniqid(mt_rand(), true));
//Valid characters for a salt are [a-zA-Z0-9./]
$base64_string = base64_encode($unique_random_string);
//But not '+' which is valid in base 64 encoding (base 64 encode returns '+' when it should be '.')
$modified_base64_string = str_replace('+','.', $base64_string);
//Truncate string to the correct length
$salt = substr($modified_base64_string, 0, $length);
return $salt;
}
When I run a check of the password with the following, data isn't being saved in the session:
if(!empty($User)&& !empty($Pass)){
$Pass = password_encrypt($Pass);
$query = "SELECT * FROM users WHERE '$User' = login AND '$Pass' = password";
$result = mysqli_query($connection, $query)
or die ('Error: '.mysql_error());
//If query produces nothing
if(!$query){
$Message = "Incorrect username and/or password.";
}else{
while($row = mysqli_fetch_assoc($result)){
///TODO Store user id in session as well
$_SESSION['first_name'] = $row["first_name"];
$_SESSION['group_id'] = $row["group_id"];
$_SESSION['user_id'] = $row["user_id"];
}
redirect_to('dashboard.php');
}
}
When I use plain text passwords instead of hashing the data is saved into the session correctly, however, when I use the hashing functions, accessing the session data I intended to save produces an error which says that, for example, the $_SESSION['user_id'] is an undefined index. Is there something wrong with the hashing methods?
The code is buggy and insecure and isn't going to work - the thread will never get to the point of trying to populate the session. But your question implies that you think it is getting there and there's something wrong with the session. There isn't.
The most elementary part of fault finding is isolating the problem - there are lots of ways of doing this, but instrumenting your code is a very simple way to do so. If you had you would know that your problem has nothing to do with the session.
You've not understood how salted hashes and initialization vectors work - you need to retrieved the hash from the password based on the suplied username then run it through crypt again in PHP land. There are lots of examples on the internet on how to do this.
Your problem is that you are not escaping characters in your encrypt password function:
$2aVOhEz8P7i6 will look for the variable '2aVOhEz8P7i6' which isn't set.
EDIT:
echo password_encrypt('hello');
= $2aVOhEz8P7i6
EDIT2:
also:
"SELECT * FROM users WHERE '$User' = login AND '$Pass' = password"
should be
"SELECT * FROM users WHERE login = '$User' AND password = '$Pass'"
for readability.
I'm trying to hash the password if you sign up on my website, but it doesn't work.
This is my code to hash the password upon signing up:
$escapedName = mysql_real_escape_string($_POST['user']);
$escapedPW = mysql_real_escape_string($_POST['password']);
$salt = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
$hashedPW = hash('sha256', $escapedPW . $salt);
Then I just insert it in a database (the hashed password and the salt).
For example, when I hash Sas, with the following salt:
abac7ad23185ad19967f0d13e962197962782f0b7ec32d9889c27a93a9e800fa
This is the hashed password:
8ca5c5f31fafbf382533dbcbfc22b3635d776ec7770c7eac58d8ef9f1fa3613c
But when I try to hash the password on log in, with the exact same password and salt, this becomes the hashed pass:
6eb4b16444f18cee19db32bd29a39970e3019c5b1972a982ae4cb9a59642dffc
This is the code I use to login:
$escapedName = mysql_real_escape_string($_POST['user']);
$escapedPW = mysql_real_escape_string($_POST['password']);
$saltQuery = mysql_query("SELECT salt FROM members WHERE user='{$escapedName}'");
while($result = mysql_fetch_assoc($saltQuery)) {
$salt = $result['salt'];
}
$hashedPW = hash('sha256', $escapedPW . $salt);
$sql = mysql_query("SELECT * FROM members WHERE user='$escapedName' AND pass='$hashedPW'; ");
while ($res = mysql_fetch_assoc($query2)) {
$username = $res['user'];
$PW = $res['pass'];
}
I hope it's not too much code and I also hope you will understand my question.
Sorry I can't comment but something tells me that there is a length restriction on the salt column in your database.
Example: The salt field might only allow a 64 characters while the generated salt might be longer therefore when you save the salt it gets trimmed which ultimately changes the hashed password.
If that's the case, you might want to trim the salt before saving it.
I'd advise using PDO to make queries against your database.
Your inputs to the hash function must be different for some reason or other.
Add log output messages that print your inputs before you hash for both use cases (create user and login). Also be sure to put quotes around the inputs in your logging to show whitespace issues.
Compare the raw inputs as well as the output of the hash function in both cases, and there will be a difference somewhere. If there is no difference, and the output of the hash is the same, then there is a problem in your query that is looking up the user in the login case.
Whatever you're doing, it's insecure if you WANT the hashes to be the same! See http://php.net/crypt for proper password hashing.
All you need:
function check_password($password) {
...//get db password to compare
if (crypt($post_password, $db_results[0]['password']) == $db_results[0]['password']) {
return true;
} else { return false; }
}
I'm writing a login form, and it converts the given password to an MD5 hash with md5($password), then matches it to an already-hashed record in my database. I know for sure that the database record is correct in this case. However, it doesn't log me in and claims the password is incorrect.
Here's my code:
$password = mysql_real_escape_string($_POST["password"]);
...more code...
$passwordQuery = mysql_fetch_row(mysql_query(("SELECT password FROM users WHERE email = '$userEmail'")));
...some code...
elseif(md5($password) != $passwordQuery)
{
$_SESSION["noPass"] = "That password is incorrect.";
}
...more code after...
I tried pulling just the value of md5($password) and that matched up when I visually compared it. However, I can't get the comparison to work in PHP. Perhaps it is because the MySQL record is stored as text, and the MD5 is something else?
$passwordQuery contains and array with the hash, and not just the hash.
Instead of
elseif(md5($password) != $passwordQuery)
try
elseif(md5($password) != $passwordQuery[0])
It looks to me like you're comparing a string (the result from $_POST) with an array (the result of mysql_fetch_row). You'll probably want something like:
if (md5($password) != $passwordQuery['password') {
....
}