Having read this article and many others out there on how to not store passwords in databases and cookies, I'm wondering now how I should do it...
What I've come up so far (after reading around a bit) is taking the clear-text user password, padding it with salt till it fills up 512 bits (64 bytes => 64 chars, since the page is non-unicode), and then doing
$pwhash = hash('sha512', $saltedpw);
for ($i=0; $i<1000; $i++)
$pwhash = hash('sha512', $pwhash);
Then I would store (UserName, HashedPw, Salt) in the database, but what do I do about the cookie (to identify users that want to stay loogend-on after the session has expired)?
First, calling hash 1000 times does not help anything, once is enough.
For remembering the user login in cookie you have two options:
As has been said, you can generate a random token and store it in the database along with the user information. When a user with no session cookie enters the site, you check if there is a cookie with the token and do a DB lookup. If you found a user with such a token, log them in. You might want to do some additional checks, like whether the current IP is the same as the IP when they first logged in.
You can store the user ID in the cookie, but then you have to sign the data using a secret key to make sure the user can't just modify it. HMAC-SHA-1 is a good way to do that. The advantage is that you don't have to store any additional data in the database. You only have to verify the signature and do a lookup on the user ID. The disadvantage is that you have to make sure the signature code is secure (HMAC-SHA-1 with a longer secret key should do that).
You do not have to store the password of the user in the cookie. You can generate a long random string (similar to a sessionid) that you store in the database and in the cookie. You can change that string everytime the session expires and the user comes back. When a user accesses the site you can check the cookie value against the database and see who the user is.
In the database store only password hashcode, and cookie should contain session id, often called SID. In another table store all SID (with userID) and thats all.
But don't forget that PHP has build in very simple and usefull session api, use it better :)
Related
After logined user, I keep user ID inside cookie ($_COOKIE['user_id']) and after this mysql check if isset user_id inside cookie and if user_id is exists in DB:
SELECT * FROM users WEHERE user_id = '$_COOKIE[user_id]'
It works, but cookie value can be changed by every cliend, so client can set cookie user_id to 1 or 2,3,4 and it will be loggined, So they can hack page.
I want to hash or secure somehow my cookie user_id value. I hope you understand me.
Can you give me any suggestions?
Do not do that in a cookie. Save a hash in the cookie and store the corresponding user id in your database. You can't make the cookie secure.
To be more clear:
When the user logs in, store a unique hash for him in the database. This could be something like that: sha512('9a7fd98asf'.time().$username). This is the value you save in the cookie, too. You know the user is logged in, if he has such a token in the database and if it matches the value from the cookie. This actually is how sessions are handled.
Cookies are prone to numerous types of attacks including someone stealing a cookie from one user and presenting it later to impersonate that user. If you were to instead use sessions, you would either have to use session cookies or URL, both of which are problematic from a security point of view.
The best you could possibly do is encrypt the cookie so you can later decrypt it when you read the user_id. This will ensure that a user cannot randomly change their user_id. However, this does not protect against one user using the data from the cookie of another user.
To guard both against reply attacks (either by the same user, or by a different user), in addition to storing the user_id, you would also want to store the expiry time in the cookie. When you get back the cookie, you can decrypt and also get the time that you need. This does mean though that this type of replay attack is possible within that time during which cookie is valid.
For encryption/decryption, you can search and find how to correctly do it using the language you are employing. You will still have to test against corrupted cookies (in which case you can assume user is not authenticated).
You want to use a meaningless token. That's the most secure way, because the token is inherently meaningless and cannot be attacked in itself. Because the token also needs to be stored on the server with associated data, this also gives the server the ultimate control over logins; because the server can revoke active tokens at any time (you cannot revoke a cookie which stores just a self-contained user id).
A standard PHP session is such a meaningless token and works just fine for most cases. Sessions can even be configured to last extremely long, and you can even change the session storage backend to a database or any custom backend you want.
If, for whatever reason, you need something else, you still want the same idea:
generate a completely random, meaningless value
store it in a database, together with what user it belongs to
send the token in a cookie to the client
You could store an additional cookie, named 'user_hash':
$_COOKIE['user_hash']=sha1($user_id . md5($user_pass . $salt1) . $salt2);
Using some constant $salt values.
When using the user_id cookie, first check if user_hash matches the user login information stored in the database.
Since the sha1 is irreversible, the password cannot be derived from it.
I'm at the beginning of a project that will have a "remember me" option when the user authenticates.
My user's database table have the following basic structure for authentication:
id : a random number generated when user is registered. (key)
username : an username choosen by the user, can be changed to another unique username.
hash : the hash of the password created with "phpass" when the password is set.
I'd like to know a safe way to keep the user logged in, even in more than one computer, using cookies and MySQL.
I wish I can use the same cookies to store a temporaly session or a "remember me" section. I've read about tokens and about hashing all user information to a cookie but is it safe? I checked Facebook's cookies that are used after user authentication and there's user id entry, is it really necessary for what I whant to do?
If I decide to hash information to the cookie should I use "phpass" that probably is slower or a simple MD5 function, seen that the authentication verification wiil be in every page and every AJAX request? Should I renew a user token every time I verify for login?
Finally, what's the best choice I have? I know there's a lot of questions similar to this but I didn't found something like "the best way" to do this. In every post I find there's something different and contradictory about this subject. I'd like to know a safe and clean way to do it.
I would recommend using a separate table to store saved login sessions. This table will store a unique hash generated at the time of user choosing to "remember me", the user's IP address, and their user_id.
When or If a user decides to keep a persistent login, you will generate the hash, save that hash as a cookie value, and store their IP / user_id in the "remembered" table.
In your session_start/check sequence, first check if session is set -- if not, check if cookie is set - if cookie is set, match their IP address to the cookie's hash value against the database and then if all checks out, set their session status as "authenticated".
The reason for having a table using their IP address and a unique hash is to allow multiple devices/computers to be persistent. Each device would have an entry in your remembered_sessions table.
This method does not store any passwords in a cookie, and only those who have successfully entered their password will have this cookie on their computer. Additionally even if someone grabbed that user's hash in their cookie they would need to be on the same network to be considered authenticated.
I want to have an auto login option check for a user. Basically that means a cookie will be stored on the client side.
Now the question is, how do I make it secure so the cookie will can not be spoofed/modified.
One of my friends suggest having a db table that stores the session_id, user's ip, browser info, etc and then compare it all that information once a user goes to the website again.
I feel like having a separate table for that is a bit too much trouble. Is there another way to do it? Maybe with tokens or something like that?
The more secure you want this infamous cookie, the more trouble it's going to be for you. If your users should be particularly secure, you will have to go with the most troublesome approach.
You should only accept this cookie with https if you want to be as secure as possible. If the cookie is accepted over http, it can be sniffed and stolen.
I would recommend that the cookie have no user data at all (a token, as you suggested). This will, unfortunately, require another table. When a user logs in and chooses "keep login," create an entry in this table. The entry can be any meaningless value (such as md5(uniqid('', true));. This token can be unique in the DB and mapped to a user's ID.
When a user visits your website, you can check the value of that cookie and get the user it belongs to and log them in. At this point, you destroy the old token and create a new one. "Destroy" can mean many things. You can delete it from the DB entirely or have a flag that disables the token. You may want to allow the same token to be used multiple times in case the cookie is received but the authentication doesn't go through for some reason, but I think this is insecure. You may also want to store the timestamp of the token and only accept it if it's been some limited period of time (30 days for example).
As your friend points out, you can store other information such as user agent, IP address, etc., but these may change even with the same browser being used (especially with mobile) and if a user's persistent login is not accepted because of this, it could be jarring and inconvenient to them.
If you really don't want to create another table, then you will have to store some way to acquire the user's ID from the cookie value. This is less secure.
For most auto-logins I know, there is a separate table to store logged-in sessions. Each auto-login session is assigned a hashed key as an identifier, the key is considerably long and virtually not possible to spoof. If you don't want users to be logged in cross-ip even with a valid code, try this.
function gen_uniqueIdent($length=32){
$alphabet = str_split('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789');
$key = '';
for($loop=0;$loop<$length;$loop++){
$i = mt_rand(0,count($alphabet)-1);
$key.= $alphabet[$i];
}
return $key;
}
Assign this value to the user cookie upon login. Then store this into the db:
function save_ident($identFromFunctionAbove,$authenticated_user_id){
//hash this with something unique to the user's machine
$hashed = md5($identFromFunctionAbove . $_SERVER['REMOTE_ADDR']);
/** Some actions to remember this hash **/
}
save that into a database correspinding with a user identity like user_id.
Now upon validation of the user cookie, you can simply:
function validateCookie(){
$ident = $_COOKIE['yourCookieName'];
$hashed = md5($ident . $_SERVER['REMOTE_ADDR']);
/** Check if this hashed value exists in db, if it does, authenticate user **/
}
You'll also need to remove the sessions after they expire or the user explicitly logs out.
Of course this is very simple, and doesn't account for md5 or ident collisions. Still, getting two 32-character random generated string to be the same as one previously generated is pretty slim a chance.
The way I have previously done this is storing the MD5 hash of the password not the actual password.
On the server side you need to check if the login is coming from a cookie and then check if the hash is the same as the password in your database after its been hashed through MD5
That way if someone hacks the users computer they can never know the value of the password however the can still use that cookie to authenticate to only your server.
You can make this more secure by making the cookie expire after x days so if the cookie is stolen the theif can only get access for that period.
At the end of the day the most and only? secure method is make the user login each time
Can't find answer to my question.
I'm creating registration and need autologin for user with cookie.
What kind of information should be stored in cookie? Is it username + hash password or what
First, just to echo what everyone else has said, this isn't so much an auto-login feature as it is a 'remember me if I navigate away from the page' feature.
How I have seen it done in the past is similar to the implementation explained by frostymarvelous. Basically I have seen 3 cookies used:
Cookie 1:
name - 'username'
value - user's name
Cookie 2:
name - 'salt'
value - random salt created for this particular login
Cookie 3:
name - 'authentication_hash'
value - Hash of a couple unique pieces of data that only your website can duplicate. If you can duplicate this value in the cookie, then make sure the user doesn't have to login again.
Basically, cookie 3 is the most important cookie and I would include a couple things in this to prevent it from being duplicated easily:
<?php
function isAuthenticationCookieValid() {
// $websitePassword would be a unique string stored in a file that is only
// accessible by the server running your website.
include("websitePassword.php");
// $hashOfUserPassword should be a hash of the user's password and should be
// retrieved from the database in hashed form because that is how you should
// store passwords.
$hashOfUserPassword = retrieveUserPasswordFromDatabase($_COOKIE['username']);
// $salt should just be read from cookie.
$salt = $_COOKIE['salt'];
$authenticationValue = sha1($websitePassword . $salt . $hashOfUserPassword);
// Compare authentication value in cookie with calculated authentication value.
return $authenticationValue == $_COOKIE['authentication_hash'];
}
?>
The contents of 'websitePassword.php' should just be:
<?php
$websitePassword = "secretWebsitePassword"; // Obviously use a better password
?>
I would also suggest making the cookies expire after a timelimit to make your website more secure and you could possibly add a time element to your hash so if they try to use that particular hash after a particular amount of time, they will not be logged in automatically.
I would not store the hash of the password in a cookie because the hashing can be reversed (at least for weaker passwords).
Generate a session token which you can store in your database and use in cookies.
See: Session token - how does it work?
What I do is, I hash the useragent, tack the
user ID the end of the string.
When a user is not logged in, I check for the cookie, split the string, verify that the useragent is the same, and then proceed to log the user ID in.
This btw is not too secure, but it is basically the way It is done. This is remember me btw.
For an auto login like xtgem, an url with the username and unique identifier is given to you. This is bookmarked and when the user clicks it, the page makes the checks and logs users in.
In most of past questions on SO about security in 'Remember me' feature in login systems, Persistent Login Cookie Best Practice is suggested.
If I understand correctly, this approach goes like this:
If user checks to remember the password, the cookie consisting the username, followed by a separator character, and some large random number should be set. For example:
$_COOKIE["login"]; ="adam:8794383bb07608636dab808df6c9e29c"
store username and hash string in database.
if both cookies exist, and are mapped to each other in db, the login is accepted.
After authentication, remove the random number from cookie, generate a new number and store in cookie. (Do I need to update this newely generated hashed string in the database as well?)
When user is logged out, current cookie number is also invalidated.(Should I remove username and the random string from the database?)
Is it all?
As in my previous question, I was told that if someone can access the cookie, they can easily authenticate. So how does this approach solves that problem?
Many thanks.
Actually, here is what I would recommend. Sample DB Schema
Users:
user_id
username
autologin_hash
autologin_expire
Process:
User clicks remember me
Server assigns a unique token and stores it in the database and sends it as a cookie.
Server also assigns a fixed expiration date in the autologin_expire field
Check to see if the user's cookie equals the one stored with their account on the server AND it has not expired
All is good...login, delete the hash, regenerate it, and update the expiration date for the next login
You never, ever want to store usernames or passwords in cookies as they are vulnerable to theft if you are not using SSL. Using a unique hash and clearing it on each login solves these problems: 1) it prevents auth details leakage, 2) it makes a auto login valid only once (cookie cannot be stolen and used again), 3) it enforce a hard expiration date server side which helps prevent abuse, 4) and the long unique ID is hard to impossible to guess so hackers would have to actually steal the cookie to gain access.
Edit: If you want even more security, make a note to clear out the hash if the user changes their password. You don't want valid auto login hashes floating around if the user changed their password out of fear their password was revealed.
4). You do need to update the hash, otherwise the person won't be automatically logged in again.
5). You should for clean up, but if it is invalidated, your code should not process it as valid.
As for stopping other people from loggin in with that cookie, you could do...
User agent must be perfect match as well.
Once logged in, their account is marked remembered login. When it comes to changing passwords, making payments, you request their password again.