How to save a hash of a password in MySQL - php

I have the following table in my database called db_pass:
id | pass
=================
1 | dalmation123
I understand that I cannot store any password in plain text format in my database, how do I go about setting up a hash? This is the code I am using below. I would appreciate some help on how to change my table db_pass as well.
if(isset($_POST['pmsubmit']))
{
LoginSubmit('pm', 'pmname', 'pmpass');
}
if(isset($_POST['tssubmit']))
{
LoginSubmit('ts', 'dept', 'tspass');
}
function LoginSubmit($pm_or_ts, $the_name_input, $the_pass_input)
{
global $pdo;
$posted_name = $_POST[$the_name_input];
$posted_pass = $_POST[$the_pass_input];
// check if password matches the one in the table
$query = $pdo->prepare("SELECT * FROM db_pass WHERE pass = :pass");
$query->execute(array(":pass" => $posted_pass));
// if there is a match then we log in the user
if ($query->rowCount() > 0)
{
// session stuff
$_SESSION[$the_name] = $posted_name;
// refresh page
header( 'Location: ' . $pm_or_ts . '/index.php' ) ;
exit;
}
// if there is no match then we present the user with an error
else
{
echo "error";
exit;
}
}

$query = $pdo->prepare("SELECT * FROM db_pass WHERE pass = :pass");
$query->execute(array(":pass" => crypt($posted_pass)));
Don't ask me which algorithm crypt actually uses. The manual entry is totally nonsensical - apparently just checking the value of a constant changes the algorithm used by crypt() which is ridiculous ....
And it's alright people saying bcrypt. But bcrypt isn't a core PHP function. If they mean write your own, then it's a stupid idea - because your implementation would undoubtedly have flaws. If they mean a library they need to point one out - PHPass is commonly recommended, but I have no knowledge to recommend it myself.
It's hardly surprising most people still use sha1 is it?

It all comes down to this. you need to perform an operation on the user password before you save it to the database. you then perform the same operation on the submitted password before checking the if that password is valid for the username/password combination.
in most cases the "operation" is a hashing or encryption process such as MD5 or bcrypt

In MySQL you could use the BINARY type to actually store hashes. A simple hash table in MySQL could look like:
CREATE TABLE IF NOT EXISTS `hastable` (
`hash` binary(20) NOT NULL,
`value` blob NOT NULL,
PRIMARY KEY (`hash`)
);
For example a SHA1 hash is always 160 bits/20 bytes long and could be stored such a binary column. Using PHP, you could get the hash as follows: hash( 'sha1', $key, true );
But that has nothing to do with storing passwords…

Related

$_GET[...] not working - database not updating

I've asked a few questions about this piece of code on this site.
Basically, my database is not updating when I use - $id = $GET_['id']; (at the top of the code below). The id gets passed to this page from the previous page - the url of the page is 'http:// www.21orange.com/CCC/changepassword.php?id=1'. There is an 'id' field in my database.
When I change the above line of code to - $id = '1' - the code runs perfectly and the database is updated. It only stops working when I use the $GET_['id']. Why is this?
// First we execute our common code to connection to the database and start the session
require("common.php");
$id = $_GET['id'];
// This if statement checks to determine whether the registration form has been submitted
// If it has, then the registration code is run, otherwise the form is displayed
if(!empty($_POST))
{
// Ensure that the user has entered a non-empty password
if(empty($_POST['password']))
{
die("Please enter a password.");
}
// Ensure that the user has entered a non-empty username
if(empty($_POST['confirmpassword']))
{
// Note that die() is generally a terrible way of handling user errors
// like this. It is much better to display the error with the form
// and allow the user to correct their mistake. However, that is an
// exercise for you to implement yourself.
die("Please confirm your password.");
}
if ($_POST['password'] == $_POST['confirmpassword']) {
// An INSERT query is used to add new rows to a database table.
// Again, we are using special tokens (technically called parameters) to
// protect against SQL injection attacks.
$query = "UPDATE Staff SET password=:password, salt=:salt WHERE id=:id";
// A salt is randomly generated here to protect again brute force attacks
// and rainbow table attacks. The following statement generates a hex
// representation of an 8 byte salt. Representing this in hex provides
// no additional security, but makes it easier for humans to read.
$salt = dechex(mt_rand(0, 2147483647)) . dechex(mt_rand(0, 2147483647));
// This hashes the password with the salt so that it can be stored securely
// in your database. The output of this next statement is a 64 byte hex
// string representing the 32 byte sha256 hash of the password. The original
// password cannot be recovered from the hash.
$password = hash('sha256', $_POST['password'] . $salt);
// Next we hash the hash value 65536 more times. The purpose of this is to
// protect against brute force attacks. Now an attacker must compute the hash 65537
// times for each guess they make against a password, whereas if the password
// were hashed only once the attacker would have been able to make 65537 different
// guesses in the same amount of time instead of only one.
for($round = 0; $round < 65536; $round++)
{
$password = hash('sha256', $password . $salt);
}
try
{
// Execute the query to create the user
$stmt = $db->prepare($query);
$stmt->execute(array(
'password' => $password,
'salt' => $salt,
'id' => $id));
}
catch(PDOException $ex)
{
// Note: On a production website, you should not output $ex->getMessage().
// It may provide an attacker with helpful information about your code.
die("Failed to run query: " . $ex->getMessage());
}
// This redirects the user back to the login page after they register
header("Location: stafflist.php");
// Calling die or exit after performing a redirect using the header function
// is critical. The rest of your PHP script will continue to execute and
// will be sent to the user if you do not die or exit.
die("Redirecting to stafflist.php");
}
die("Passwords do not match.");
}
I'm new to php, so forgive my naivety. P.s. I know the method I'm using is fairly old school, but it's only a test.
Thanks,
Joe
You can't do both GET and POST in a single HTTP Request.
However, you can use an hidden input field to get around this limitation:
In your HTML markup, you can add the following:
<input type="hidden" name="id"
value="<?php echo htmlspecialchars($_GET['id'], ENT_QUOTES); ?>" />
And your $_GET['id'] should work just fine.
To avoide this error
Undefined index: id in /home/content/47/11368447/html/CCC/changepassword.php on line 6
first test if the index exists :
if(isset($_GET['id'])) {
$id = $_GET['id'];
} else {
// here you can set a value for the id
}
Otherwise, you can add your $id var in your if test :
if(!empty($_POST) && $id)
{
//...
}
It looks like you are passing the 'id' to the action URL but for some reason the $_GET variable is not having it. Please double check:
Are you really passing the 'id' to the URL ? Please make sure.
Please check the code in common.php to see if the $_GET variable is modified in it.
Is that script behind a rewrite settings (e.g in .htaccess) ? if yes, $_GET parameters could be gone due to inappropriate rewrite settings. You can further test it out by putting print_r($_GET); in the beginning and visit that script directly ( GET instead of POST )
$id = $_GET['id'];
first check there is any value in $id to print the $id through echo

convert plain text password login to hash [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Secure hash and salt for PHP passwords
I am working on a PHP script for my site, the site it's self is up and running. The problem I have is that the registration on the website is for plain text passwords. Obviously this is very weak security. I am hoping someone can help me convert it so I can use a hash password.
I have included part of the registration code that I think counts. I did not include all code on the page as I did not think it relevant but will supply if someone thinks it will help.
require 'include.inc';
if ($signup) {
if ($signup[repassword] != $signup[password]) {
$err_msg = "Your passwords do not match.";
error($err_msg);
}
if(!preg_match("^[_\.0-9a-z-]+$/i^", $str)) {
$msg = 'Invalid Username! Usernames can consist of letters and numbers only';
}
if(!preg_match("^[_\.0-9a-z-]+$^",$signup[password])) {
$err_msg = "Invalid Password! Passwords can consist of letters and numbers only.";
}
if(!$signup[password] || !$signup[username] || !$signup[email] || !$signup[username])
$err_msg = "Oops! You forgot some important fields!";
if (!$err_msg) {
$usercheck = #mysql_query("INSERT INTO user values(
'NULL','$signup[fname]','$signup[lname]',
'$signup[username]','$signup[password]','$signup[email]', 1, ".$pointInc.", '$signup[referral]', NOW(), 'n', 'y')");
// done, you are entered correctly, Now Enter the points and URL info
$sql = "Select id from user where username='$signup[username]'";
$result = mysql_query( $sql );
if ( $result != false )
{
while ( $data = mysql_fetch_assoc( $result ) )
{
$point_set = $data['id'];
}
} else {
echo mysql_error();
}
// add rerral points
if ($signup[referral]) {
$referralSql="UPDATE points SET points=points+ ".$refPoints . " WHERE userid=".$signup[referral];
$result = mysql_query( $referralSql );
if ( $result != false )
{
} else {
echo mysql_error();
}
}
// add URL
$sql="INSERT INTO url_table ( userid, website, active, datechanged) VALUES ($point_set,'".$signup[site_url]."','n', '".date("Ymd")."')";
$result = mysql_query( $sql );
if ( $result != false )
{
} else {
echo mysql_error();
}
// add points
$sql="INSERT INTO points (userid, username, points) VALUES ($point_set,' ',$signPoints)";
$result = mysql_query( $sql );
if ( $result != false )
{
} else {
echo mysql_error();
}
}
echo mysql_errno().": ".mysql_error()."<br>";
if (!$usercheck) {
$err_msg = "Database error:<br>There was an error entering your account.<br>It is possible that username or Email already exists, please try another one.<br>";
} else {
include ("reg.php");
exit;
}
}
if (!$err_msg) {
// done, you are entered correctly
}
pageHeader($title, $bgColor, $styleSheet);
?>
The basic principle is quite easy:
1 . When a user registers, don't store the plaintext password, but hash(password).
$query = $pdoObject->prepare('INSERT INTO users (UserName, Password) VALUES (:u, :p)');
$query->bindParam(':u', $_POST['username']);
$query->bindParam(':p', hash($_POST['password']));
// Note that we don't store the password, we store the hash of the password. Once this script is done executing, no one involved in the website (except for the original user) will know that the password is
$query->execute();
2 . When a user attempts to log in, calculate the hash of the entered password with the hash stored in your user database. If both hashes match, the password is correct.
$query = $pdoObject->prepare('SELECT * FROM users WHERE UserName = :u AND Password = :p');
$query->bindParam(':u', $_POST['username']);
$query->bindParam(':p', hash($_POST['password']));
// We locate the original user by searching for the hash of the password that they typed in
So much for theory. Some practical issues you should consider:
Many tutorials suggest using MD5 of SHA1 as hash functions. However, these are not secure. Check the PHP hash library (especially the hash() function) for available hashing algorithms.
You should also consider using salted passwords. For each registered user, create a random string (the salt), concatenate this string with the user's password and then hash both password and salt (the salt needs to be saved too, because you need it again when authenticating the user).
You can use PDO to make your SQL more secure. This will protect you against something called SQL Injection. If you don't do this, then users will be able to mess up your website by entering characters such as apostrophes (').
You can use md5 function to create a hash for your passwords, example:
$password = '12345';
$password = md5($password);
You save this hash at the database and when you will check user and password for login you do this
$post['password'] = md5($post['password']);
and check if is equal of the hash saved in the database.
I know that md5 is not the best hash but is simple and has a good level of security
Use crypt for hashing passwords.
Use md5 for that http://php.net/manual/en/function.md5.php
Also look up uniqid http://php.net/manual/en/function.uniqid.php
It's always a bad practice to store the password in plain text in your database. Check this other question:
What are the best practices to encrypt passwords stored in MySql using PhP?
some opinions don't think md5 or SHA-1 breakable, try to salt them.. search that.

What am I doing wrong with this crypt()-based bcrypt authentication?

I've cribbed this code almost verbatim from a bunch of very helpful answers here on SO, so I can't get my head around what's wrong.
First, here's my function for creating a user account:
function BFcrypt($password,$cost)
{
$chars='./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
$salt=sprintf('$2a$%02d$',$cost);
for($i=0;$i<22;$i++) $salt.=$chars[rand(0,63)];
return array(
'salt'=>$salt,
'hash'=>crypt($password,$salt)
);
}
Then, when a user goes to login:
case 'login':
$login =$_POST['login'];
$pwd =$_POST['pwd'];
$sql ="SELECT * FROM `users` WHERE `users`.`login`='$login' LIMIT 1;";
if($query = mysql_query($sql)){
$row=mysql_fetch_assoc($query);
print_r($_POST);
print_r($row);
$hash = $row['password'];
if(crypt($pwd,$hash)==$hash){
echo"SUCCESS";
}else{
echo"FAILURE";
}
}
The login function appears to always be failing. I've set it to show me $pwd, $hash and crypt($pwd,$hash), and for some reason, crypt($pwd,$hash) never seems to == $hash.
Here's a row in the database for a sample user (I'm logging the salt now, though I know it's supposed to be included in the hash:
'id'=>'680',
'login'=>'argh',
'password'=>'$2a$10$BWZAX7wrwQp5iyK4kh6VLunqy82eiXg7GaDs6mJLqdgT5s2qiUqYW',
'salt'=>'$2a$10$BWZAX7wrwQp5iyK4kh6VL5',
'first'=>'argh',
'last'=>'argh',
'zip'=>'00000',
'email'=>'argh',
'date updated'=>'2012-12-12 16:05:29'
I believe that when I call crypt($pwd,$hash),it truncates $hash, leaving only the original 22-character salt (plus prefix), thus the output will be the same as $hash as long as $pwd is the same. I'm seeing clearly there's an issue here in that the salt I'm recording is one character longer than the one that ends up appended to the hash, but it's the appropriate length for blowfish, and anyway, making it one character shorter doesn't seem to help.
I can't figure out what I'm doing wrong here. Any help would be appreciated.
Based on your own salt value and password of 'argh' I ran a small test script:
$hash = crypt('argh', '$2a$10$BWZAX7wrwQp5iyK4kh6VL5');
// $2a$10$BWZAX7wrwQp5iyK4kh6VLuIzJHihvZTdfpRXNkTPVKkTiGfLDl1RO
var_dump(crypt('argh', $hash) == $hash);
// bool(true)
The problem doesn't seem to be in the code you've shown.
You could check your database field width to store the password hash, which should be at least 60 wide. And while you're at it, fix your SQL injection vulnerability (by using prepared statements most preferably).

PHP, MySQL - My own version of SALT (I call salty) - Login Issue

Ok I wrote my own version of SALT I call it salty lol don't make fun of me.. Anyway the registration part of my script as follows is working 100% correctly.
//generate SALTY my own version of SALT and I likes me salt.. lol
function rand_string( $length ) {
$chars = "ABCDEFGHIJKLMNOPQRSTUWXYZabcdefghijklmnopqrstuwxyz1234567890";
$size = strlen( $chars );
for( $i = 0; $i < $length; $i++ ) {
$str .= $chars[ rand( 0, $size - 1 ) ];
}
return $str;
}
$salty = rand_string( 256 );
//generate my extra salty pw
$password = crypt('password');
$hash = $password . $salty;
$newpass = $hash;
//insert the data in the database
include ('../../scripts/dbconnect.php');
//Update db record with my salty pw ;)
// TESTED WITH AND WITHOUT SALTY
//HENCE $password and $newpass
mysql_query("UPDATE `Register` SET `Password` = '$password' WHERE `emailinput` = '$email'");
mysql_close($connect);
However my LOGIN script is failing. I have it setup to TEST and echo if its login or not. It always returns FAILED. I entered the DB and changed the crypted salty pw to "TEST" and I got a SUCCESS. So my problem is somewhere in this LOGIN script I assume. Now I am not sure how to implement my $Salty in this. But also be advised that even without SALTY (just using crypt to store my pass) - I was still unable to perform a login successfully. And if you're gonna suggest i use blowfish - note that my webhost doesn't have it supported and i don't know how to install it.
here's my login script:
if (isset($_POST['formsubmitted']))
{
include ('../../scripts/dbconnect.php');
$username = mysql_real_escape_string($_POST['username']);
$password = crypt(mysql_real_escape_string($_POST['password']));
$qry = "SELECT ID FROM Register WHERE emailinput='$username' AND Password='$password'";
$result = mysql_query($qry);
if(mysql_num_rows($result) > 0)
{
echo 'SUCCESS';
//START SESSION
}
else
{
echo 'FAILED';
//YOU ARE NOT LOGGED IN
}
}
So what's wrong with this login? Why isn't it working just using the crypt/storing only crypt?
How can i make it work storing both the crypt and randomly generated SALTY :) ?
Ty advance
Every time this code runs, you will be generating a DIFFERENT salt value. So when the password is first stored, your salt will be (making up some example stuff) abc. You crypt your password (say it comes out to be 123, attach the salt, and end up with 123abc, which you store in the database.
When you try to log in next time, you generate a new random salt (say, 456). You crypt the password, append the salt, and now you've got 123456. And there's your problem. now you've got two completely different strings, so your login fails.
You need to store the salt separately from the crypted text, so you can REUSE the salt again later. That means your login script first has to retrieve the salt associated with whatever user is trying to log in, recrypt/salt the password they entered, and see if the strings match.
And in a bigger picture view, you're also using the salting incorrectly. The salt must be added to the password BEFORE you run the password through crypt. Salts are there to increase the difficulty in brute forcing the password. 123 is a simple password, but 123big_long_ugly_salt_value isn't.
All you're doing is some useless random number generation that only increases your cpu and storage overhead, but does nothing to increase security.
So in the storing you are crypting 'password' then appending $salty (which is a rand_string)
However in the password compare later you are crypting $_POST['password'], and not appending the same random string. So they would not be the same.
Instead of generating the salt randomly with each password storing procedure you will end up storing a salt somewhere, or creating the salt from something that you can be sure to find again later for the comparison when the login happens again.
Some ideas of how to get this working now would be for you could use the user id, or or some other info that will not change as your salt if you want each user to have a different salt. Or if that doesn't matter you could use some other random string of your own to use.
And as Mark B said in his answer, you should append the salt BEFORE you crypt. Appending it after really is not securing the password.
When using the crypt function you can add your salt right in the function call:
crypt($password, $salt);
And I would not really mess with the mysql_real_escape_string for the password, since you are crypting it. There is not really any worries about injection since you are crypting the possible naughty string.
I would make the following tweaks to your code:
function rand_string( $length ) {
// added period and slash to the alphabet
$chars = "ABCDEFGHIJKLMNOPQRSTUWXYZabcdefghijklmnopqrstuwxyz1234567890./";
$size = strlen( $chars );
for( $i = 0; $i < $length; $i++ ) {
$str .= $chars[ rand( 0, $size - 1 ) ];
}
return $str;
}
// we need 22 random characters
$salty = rand_string(22);
// apply blowfish with cost := 13
$newpass = crypt($password, sprintf('$2y$%02d$%s', 13, $salty));
This uses Blowfish to hash your password; it takes roughly 0.5s to complete at strength 13, so depending on your situation you may want to lessen it; the cost can be changed for newer passwords.
The salt is stored together with the password btw, so there's no need to have another column for that.
To verify the password from database you have to first load the password field from the respective Register row.
if (crypt($_POST['password'], $password_from_db) === $password_from_db) {
// success
} else {
// password didn't match
}
Btw, the comparison function is often turned into a constant time algorithm to prevent timing attacks.
Your salty() function can be replaced with the following equivalent to produce 22 char long salts:
substr(strtr(base64_encode(openssl_pseudo_random_bytes(18)), '+', '.'), 0, 22);
See also: https://wiki.php.net/rfc/password_hash

PHP login security Idea

I have an idea for a system to log in users and validate their login on pages.
I realize that there are lots of systems out there, but I mainly was curious if the idea I had was any good. I've done some digging, but most results seem to leave out what I've always thought to be important practices (like password encryption, etc). I'll probably look harder for a pre-made solution, as it is probably more secure, but I haven't really worked with application security, and was hoping to get some feedback.
When a user logs in, their name and password are verified against a database, the password is encrypted using SHA256 and a randomly generated salt, the overall string (both the salt and the encrypted password is 128 chars. long).
Here's the password validation code:
function ValidatePassword($password, $correctHash)
{
$salt = substr($correctHash, 0, 64); //get the salt from the front of the hash
$validHash = substr($correctHash, 64, 64); //the SHA256
$testHash = hash("sha256", $salt . $password); //hash the password being tested
//if the hashes are exactly the same, the password is valid
return $testHash === $validHash;
}
If the login is valid, they are assigned a token. This token is similar to the password encryption, but stores the encrypted epoch as well as another random salt. The token, the login time, an expiration time, and the username are stored in a DB and the username and the token are transmitted as session information.
Here's the code that creates the token:
function loginUser($email)
{
$thetime = time();
$ip = $_SERVER['REMOTE_ADDR'];
$dbuser="///";
$dbpass="///";
$dbtable="tokens";
mysql_connect(localhost,$dbuser,$dbpass);
mysql_select_db("///") or die( "Unable to select database");
//Generate a salt
$salt = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
//Hash the salt and the current time to get a random token
$hash = hash("sha256", $salt . $password);
//Prepend the salt to the hash
$final = $salt . $hash;
$exptime = $thetime + 3600;
//Store this value into the db
$query = "INSERT INTO `spanel`.`tokens` VALUES ('$final', $thetime, $exptime, $thetime, '$ip', MD5('$email') )";
mysql_query($query) or die ("Could not create token.");
//Store the data into session vars
$_SESSION['spanel_email'] = $email;
$_SESSION['spanel_token'] = $final;
return true;
}
When they reach a page, the token they have and the username are checked against the DB. If the check is good, the expiration time is updated and the page loads.
Here's that code:
function validateUser($page)
{
//Grab some vars
$thetime = time();
$ip = $_SERVER['REMOTE_ADDR'];
$token = $_SESSION['spanel_token'];
$email = $_SESSION['spanel_email'];
$dbuser="///";
$dbpass="///";
$dbtable="tokens";
mysql_connect(localhost,$dbuser,$dbpass);
mysql_select_db("///") or die( "Unable to select database");
//Global var
//Get the var for token expire
$token_expire = 3600;
//Validate the token
$query = "SELECT * FROM `tokens` WHERE `token` LIKE '$token' AND `user_id` LIKE MD5('$email') AND `exp` > $thetime";
$result = mysql_query($query) or die(mysql_error());
//Check if we have a valid result
if ( mysql_num_rows($result) != 1 ) {
//Logout the user
//Destroy the session
session_destroy();
//Redirect
header("location: /spanel/login.php?denied=1");
exit();
//(Since the token is already invalid, there's no reason to reset it as invalid)
}
$row = mysql_fetch_assoc($result);
//Update the token with our lastseen
$newexp = $thetime + $token_expire;
$query = "UPDATE `spanel`.`tokens` SET `exp` = $newexp, `lastseen_ip` = $thetime, `lastseen_ip` = '$ip' WHERE `token` LIKE '$token'";
mysql_query($query);
}
Feedback (good and bad) is appreciated. Like I said, I haven't done much security and was hoping to get pointers.
EDIT: I fear I overestimated my ability to effectively create a login system. Saying this, I understand if you decide to stop trying to figure out the jumbled mess that was this probably flawed idea.
Nevertheless, here's the php code from the login page. (After what's been said here, I realize just POST'ing the password is a big no-no).
$email = $_POST['email'];
$password = $_POST['password'];
$dbuser="///";
$dbpass="///";
$dbtable="///";
mysql_connect(localhost,$dbuser,$dbpass);
mysql_select_db("spanel") or die( "Unable to select database");
$query = "SELECT * FROM users WHERE `email` LIKE '$email'";
$result=mysql_query($query) or die(mysql_error());
$num=mysql_num_rows($result);
$row = mysql_fetch_array($result);
if ( ValidatePassword($password, $row['hash']) == true ) {
loginUser($email);
header("location: /spanel/index.php");
} else {
echo "<p>Login Failed.</p>";
}
Here's the bit that generates the password salt and hash when the account is created.
function HashPassword($password)
{
$salt = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM)); //get 256 random bits in hex
$hash = hash("sha256", $salt . $password); //prepend the salt, then hash
//store the salt and hash in the same string, so only 1 DB column is needed
$final = $salt . $hash;
return $final;
}
Thanks for the feedback, I'm glad the problems with my lack of knowledge were found here and not after an attack.
For one, hashing like that is insecure. sha256 can be broken easily, at least for short passwords. You must use some hash stretching. See if you can find some PBKDF2 implementations or use the wikipedia's suggestion for a "for loop" with sha256.
Moreover I don't get what you achieve with having more session variables. I don't understand what validateuser() does, it still relies on session id, or am I missing something.
Similar to sivann, I can’t see the reason for the additional spanel_token either. All it seems to effectively do is to ensure that the session is no longer valid after the token’s expiration time. Since both values for token and user_id for the WHERE condition are stored in the session and are only set during the login, they won’t change. But session expiration can be implemented much easier.
But apart from that and much more important: your code is vulnerable to SQL injection. It would be easy with the knowledge you’ve posted here. All you need is to do the following steps:
Find out the number of columns of users with a UNION SELECT injection in email:
' UNION SELECT null, …, null WHERE ''='
If the wrong number of columns is entered, your script will throw a MySQL error, otherwise the “Login Failed.” will appear. Thanks for that.
By using the following query:
SELECT t1.* FROM users t1 RIGHT JOIN (SELECT email, '000000000000000000000000000000000000000000000000000000000000000060e05bd1b195af2f94112fa7197a5c88289058840ce7c6df9693756bc6250f55' hash FROM users LIMIT 1) t2 USING (email);
the value 000000000000000000000000000000000000000000000000000000000000000060e05bd1b195af2f94112fa7197a5c88289058840ce7c6df9693756bc6250f55 is injected into each record instead of the original hash column value. The leading 0s are the salt and the remaining string is the salted SHA-256 hash value for an empty password string, which will result in a valid password.
So we end up with entering an empty string for the password field and the following for the email field:
' UNION SELECT t2.* FROM users t1 RIGHT JOIN (SELECT email, '000000000000000000000000000000000000000000000000000000000000000060e05bd1b195af2f94112fa7197a5c88289058840ce7c6df9693756bc6250f55' hash FROM users LIMIT 1) t2 USING (email) WHERE ''='
This should suffice to get ‘authenticated’ as any user.
Just commenting on the salt/hash system: Storing the salt alongside the password in the database sort of defeats the purpose of salting--if you database is compromised the salt becomes useless from a security perspective, because its right there to aid in guessing / breaking the security. The purpose of a salt is to increase the time taken to guess a hashed word by adding a secret string of appropriate length to the value you are hashing.

Categories