I have a form for updating user data. It posts to this page:
<?php
//Update user table
session_start();
include 'sql_connect_R.inc.php';
$id = mysql_real_escape_string($_POST['userID']);
$password = mysql_real_escape_string($_POST['user_passwrd']);
$salt = time();
$hash = sha1($password . $salt);
mysql_query("UPDATE users SET user_passwrd = '$hash', stamp = '$salt', pending = 'yes'
WHERE userID = '$id'");
mysql_close($con);
?>
(I have edited out the things not pertinent to this question)
I believe what is happening is when the 'stamp' field is being populated with the $salt it is getting a different value than when the $hash is being calculated. Therefore, when a user signs in and is checked here:
$qry="SELECT * FROM users WHERE userlogin = '$login' AND user_passwrd = sha1(CONCAT('$password', stamp))";
$result=mysql_query($qry);
$row = mysql_fetch_assoc($result);
$num = mysql_num_rows($result);
When I echo $num it returns a value of 0.
I'm wondering if there is a way to ensure that the value of $salt remains the same when it is being used in $hash and then when it is updating the field 'stamp'.
Can anyone help me with this or point me in the right direction? Thanks in advance.
Cheers
More ideas so I've changed my comment into an answer...
It's worth noting that you're using PHP's SHA1 function when storing but mysql's when retrieving. They should be the same but that's the first place I'd look to debug this. try using mysql's sha function to store the hash or retrieve the record based on login, read the salt and hash it in PHP to compare
How are you storing the timestamp? Is it possible that it's being transformed/rounded/clipped/treated as a date string in some way? Just for a sanity check, take the string you're feeding into the sha1 function in both steps and check they're identical.
Further to your comment, can you post the schema for the relevant fields in the table?
Thank you for all comments. I want to report that I've 'solved' the problem. I had made a change in the name of the password input field late one night and neglected to change the $_POST value. What this did, of course, was not supply the $password value to the $hash. Though I'm embarrassed about this, I think it is important for me to share my oversight to exemplify how important it is to check ALL places where errors can occur. I failed to double-check everything and made incorrect assumptions about the nature of the problem. The code worked fine, it was the loose screw in front of the keyboard that caused the problems. Cheers
You're doing your queries incorrectly. You need to concatenate the variables in the string and NOT use single quotes. Use the quote to the left of your 1 key ``. This is the way that most MySQL read queries. example:
<?php
//Update user table
session_start();
include 'sql_connect_R.inc.php';
$id = mysql_real_escape_string($_POST['userID']);
$password = mysql_real_escape_string($_POST['user_passwrd']);
$salt = time();
$hash = sha1($password . $salt);
mysql_query("UPDATE `users` SET `user_passwrd` = '".$hash."', `stamp` = '".$salt."', `pending` = 'yes' WHERE `userID` = '".$id."'");
mysql_close($con);
?>
$qry="SELECT * FROM `users` WHERE `userlogin` = '".$login."' AND `user_passwrd` = '".sha1(CONCAT($password, stamp))".'";
$result=mysql_query($qry);
$row = mysql_fetch_assoc($result);
$num = mysql_num_rows($result);
This little change should help. Sometimes the db can be a little touchy. I hope this helps.
Related
I am creating a login system for my website using a mysql database.
When the user registers, it saves the password to the database using:
$password = hash("sha512","somesalt".$password."moresalt");
Then when I login, I compare the password entered to the password in the database using the same hash function.
To compare the database I use this:
$query = mysql_query("select * from users where password='$password' AND email='$email'", $connection);
$rows = mysql_num_rows($query);
if ($rows == 1) {//do login stuff}
But rows always returns 0. When I remove the hash function from both the register and login, it logs in fine. What's wrong?
As a side note in case anyone's wondering, I would be using mysqli but my webhosting's database version is old. They are using 5.2 I believe.
I forgot to mention that I did check to make sure the database did match what it was getting as seen in these pics (can't embed pics so links):
https://drive.google.com/file/d/0B_u6weYp5wTCQng5eVhTSkZFRDg/view?usp=sharing
https://drive.google.com/file/d/0B_u6weYp5wTCQVRXTkNqdzhWUFE/view?usp=sharing
what is the length of your password field in database???
The reason seems to me is the hashed password length is too long and while saving to database part or it is dropped...
Then when you compare you get 0 rows...
Okay, I would like to suggest that you use prepared statements other than the mysql library. It is much more secure and reliable.
$query = "SELECT * FROM `users` WHERE AND `email`=:email_token";
Then you prepare and execute your query
$data = $connection->prepare($query);
try {
$data->bindParam(":email_token",$_POST["email"],PDO::PARAM_STR);
$data->execute();
}
$result = $data->fetch(PDO::FETCH_ASSOC);
while($row = $result) {
$out = $row["password"];
}
if($out == $_POST["password"]) {
//loggin
} else {
//get lost
}
This is a very basic structure but essentially you want to pull the password out of the database first then compare the strings instead of doing it all with your query.
The code will display the returned values and and if it is greater than one it will return "Yes". But I am having trouble with the WHERE clause in $check. When I take it out the code works just fine but when I add it, the page returns incorrect values. Any ideas what's wrong?
<?php
$con = mysqli_connect("127.0.0.1","root","","lian");
$u= $_GET['username'];
$pw = $_GET['password'];
$check = "SELECT username,password FROM users WHERE username='$u' AND password='$pw'";
$login = mysqli_query($con,$check) or die(mysqli_error($con));
$num_rows = mysqli_num_rows($login);
echo "$num_rows \n";
if (mysqli_num_rows($login) == 1) {
$row = mysqli_fetch_assoc($login);
echo 'Yes';
exit;
}
else {
echo 'No';
exit;
}
Leaving aside the injection vulnerabilities, it may be because of special characters or whitespace. Try trim'ing your GET values.
$u = trim($_GET['username']);
$pwd = trim($_GET['password']);
Are you getting the number of results as 0? Also try echoing the statement in a development environment to check exactly what the statement is.
Try like this
$u= trim(mysqli_real_escape_string($_GET['username']));
$pw = trim(mysqli_real_escape_string($_GET['password']));
$check = "SELECT username,password FROM users WHERE username='$u' AND password='$pw'";
Also I hope you are ensuring unique combination of username and password.
Because suppose there are two entries in your users table
username="abc" password ="12345"
Then mysqli_num_rows() function will return two rows and the
if (mysqli_num_rows($login) == 1)
condition will return false meaning the user desn't exist.
The above comments are valid to improve the security of your code and protect vs sql injection.
Regarding your actual problem if the code executes correctly when you don't have the where clause in place but fails when you do there are a couple of possibilities:
The username or password are wrong - where wrong can mean they have extra whitespace, case insensitivities or that the column names are incorrect(case sensitive database?)
The string you are passing to the server is not displaying correctly.
Check both options by doing an echo of $u, $pw and $check right after you form your SQL string. If it's still not clear then copy whatever is echoed for $check and past it directly into the parser(management studio I guess?) and see what it returns.
Good Luck.
The code below is supposed to check if there is a person in the database with a row in the database with the username it gets from the cookie login.And if there is it is supposed to include a page and if there isn't a person in the database with this user_id it is supposed to echo.Here is my code so far please tell me how I would do this.I also already know before someone tells me that mySQL statements like I have it are becoming depreciated.Here is My code:
<?php
include("dbconnect.php");
mysql_select_db("maxgee_close2");
$username = $_COOKIE['maxgee_me_user'];
$result = mysql_query("select user_id from users where username = '$username'");
$row = mysql_fetch_array($result);
mysql_free_result($result);
$check = mysql_query("SELECT * FROM events_main WHERE user_id ='$row['user_id']'") or die(mysql_error());
if(1==1){
if (mysql_num_rows($check)>0)
{
include("example.php");
}
else
{
echo "example";
}
}
?>
In the double-quoted string, your array variable $row['user_id'] is being incorrectly parsed due to the fact that you have quoted the array key without surrounding the whole thing in {}. It is permissible to omit the {} in a double-quoted string if you don't quote the array key, but the {} adds readability.
check = mysql_query("SELECT * FROM events_main WHERE user_id ='{$row['user_id']}'") or die(mysql_error());
//-------------------------------------------------------------^^^^^^^^^^^^^^^^^^
// Also acceptable, but not as tidy, and troublesome with multidimensional
// or variable keys - unquoted array key
check = mysql_query("SELECT * FROM events_main WHERE user_id ='$row[user_id]'") or die(mysql_error());
//-------------------------------------------------------------^^^^^^^^^^^^^^^^^^
As mentioned above, $_COOKIE is never considered a safe value. You must escape its values against SQL injection if you continue to use the old mysql_*() API:
$username = mysql_real_escape_string($_COOKIE['maxgee_me_user']);
2 Things right off the bat, like Waleed said you're open to SQL injection, not very nice thing to have happen to you. I would look into reading tutorials about MySQLi and PDOs, from there try and dive into a better way or running queries.
Also you are choosing to use cookies instead of sessions to store the username? Cookies can be modified client-side to say anything a smart user with firebug would want them to be. Sessions are stored server-side and the client (end-user) is only given an id of the session. They cannot modify the username if you send it as a session. (They could try and change the session id to another random bunch of numbers but thats like pissing into the wind, pardon my french.
Heres some pseduo code that will get you on your way I think
<?php
include("dbconnect.php");
$database = "maxgee_close2"; //Set the database you want to connect to
mysql_select_db($database); //Select database
$username = $_SESSION['maxgee_me_user']; //Grab the username from a server-side stored session, not a cookie!
$query = "SELECT user_id FROM `users` WHERE `username` = '" . mysql_real_escape_string($username) . "' LIMIT 1"; //Note the user of mysql_real_escape_string on the $username, we want to clean the variable of anything that could harm the database.
$result = mysql_query($query);
if ($row = mysql_fetch_array($result)) {
//Query was ran and returned a result, grab the ID
$userId = $row["user_id"];
mysql_free_result($result); //We can free the result now after we have grabbed everything we need
$query_check = "SELECT * FROM `events_main` WHERE `user_id` = '" . mysql_real_escape_string($userId) . "'";
$check = mysql_query($query_check);
if (mysql_num_rows($check)>0) {
include("example.php");
}
else {
echo "example";
}
}
?>
That code may/may not work but the real key change is that fact that you were running
mysql_free_result($result);
before your script had a chance to grab the user id from the database.
All in all, I would really go back and read some more tutorials.
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.
I'm trying to login a user, but I always get no result from the query below. If I just query the username, it works. Everything looks good in the database.
Any help is very appreciated!
Thanks
$login = 1;
$username = 'james';
$password = 'myPassword';
if ($login == 1)
{
$pwdPassword = md5($password);
$insertUser = mysql_query("INSERT INTO users (username, password) VALUES ('$username', '$pwdPassword')");
$queryUser = "SELECT * FROM users WHERE username='$username' AND password='".md5($password)."'";
$result = mysql_query($queryUser);
if (mysql_num_rows($result) != 0)
{
echo 'success';
return;
}
else
{
echo mysql_error();
echo 'error';
return;
}
}
UPDATE
If I try the above script, without using md5(), it works fine. So I guess the problem is with the md5(). Is md5() the best way to handle the password or is there any better way?
Edited Solution
You say the code works fine without md5 but it doesn't work with md5. When you change the code to encrypt the password, do you also change the database entry (manually I suppose) to the md5 hash?
Remember that all md5 does is turn your password into a garbled bunch of characters. So you need to store that garbled bunch in the database, and use that to compare to the md5 hash you create of the user's input.
make sure your password field in your DB is CHAR(32).
md5() creates a 32 bit hash
(always 32 characters), set the size of your field to 32 to avoid it being truncated when it's inserted.
Other than that I don't see anything wrong with the script. Try echo-ing what you're pulling out of the database as password and $pwdPassword - that could give you an idea of what's going wrong.
"SELECT * FROM users WHERE username='$username' AND password='".md5($password)."'";
I donn't believe you need to call the md5() function again, since you allready declared $pwdPassword
This might work:
"SELECT * FROM users WHERE username='$username' AND password='" . $pwdPassword . "'";
Password might be a reserved name, try escaping it
`password`