My web application uses sessions to store information about the user once they've logged in, and to maintain that information as they travel from page to page within the app. In this specific application, I'm storing the user_id
, first_name
and last_name
of the person.
I'd like to offer a "Keep Me Logged In" option on log in that will put a cookie on the user's machine for two weeks, that will restart their session with the same details when they return to the app.
What is the best approach for doing this? I don't want to store their user_id
in the cookie, as it seems like that would make it easy for one user to try and forge the identity of another user.
Implementing a "Keep Me Logged In" feature means you need to define exactly what that will mean to the user. In the simplest case, I would use that to mean the session has a much longer timeout: 2 days (say) instead of 2 hours. To do that, you will need your own session storage, probably in a database, so you can set custom expiry times for the session data. Then you need to make sure you set a cookie that will stick around for a few days (or longer), rather than expire when they close the browser.
I can hear you asking "why 2 days? why not 2 weeks?". This is because using a session in PHP will automatically push the expiry back. This is because a session's expiry in PHP is actually an idle timeout.
Now, having said that, I'd probably implement a harder timeout value that I store in the session itself, and out at 2 weeks or so, and add code to see that and to forcibly invalidate the session. Or at least to log them out. This will mean that the user will be asked to login periodically. Yahoo! does this.
OK, let me put this bluntly: if you're putting user data, or anything derived from user data into a cookie for this purpose, you're doing something wrong.
There. I said it. Now we can move on to the actual answer.
What's wrong with hashing user data, you ask? Well, it comes down to exposure surface and security through obscurity.
Imagine for a second that you're an attacker. You see a cryptographic cookie set for the remember-me on your session. It's 32 characters wide. Gee. That may be an MD5...
Let's also imagine for a second that they know the algorithm that you used. For example:
Now, all an attacker needs to do is brute force the "salt" (which isn't really a salt, but more on that later), and he can now generate all the fake tokens he wants with any username for his IP address! But brute-forcing a salt is hard, right? Absolutely. But modern day GPUs are exceedingly good at it. And unless you use sufficient randomness in it (make it large enough), it's going to fall quickly, and with it the keys to your castle.
In short, the only thing protecting you is the salt, which isn't really protecting you as much as you think.
But Wait!
All of that was predicated that the attacker knows the algorithm! If it's secret and confusing, then you're safe, right? WRONG. That line of thinking has a name: Security Through Obscurity, which should NEVER be relied upon.
The Better Way
The better way is to never let a user's information leave the server, except for the id.
When the user logs in, generate a large (128 to 256 bit) random token. Add that to a database table which maps the token to the userid, and then send it to the client in the cookie.
What if the attacker guesses the random token of another user?
Well, let's do some math here. We're generating a 128 bit random token. That means that there are:
Now, to show how absurdly large that number is, let's imagine every server on the internet (let's say 50,000,000 today) trying to brute-force that number at a rate of 1,000,000,000 per second each. In reality your servers would melt under such load, but let's play this out.
So 50 quadrillion guesses per second. That's fast! Right?
So 6.8 sextillion seconds...
Let's try to bring that down to more friendly numbers.
Or even better:
Yes, that's 47917 times the age of the universe...
Basically, it's not going to be cracked.
So to sum up:
The better approach that I recommend is to store the cookie with three parts.
Then, to validate:
Note: Do not use the token or combination of user and token to lookup a record in your database. Always be sure to fetch a record based on the user and use a timing-safe comparison function to compare the fetched token afterwards. More about timing attacks.
Now, it's very important that the
SECRET_KEY
be a cryptographic secret (generated by something like/dev/urandom
and/or derived from a high-entropy input). Also,GenerateRandomToken()
needs to be a strong random source (mt_rand()
is not nearly strong enough. Use a library, such as RandomLib or random_compat, ormcrypt_create_iv()
withDEV_URANDOM
)...The
hash_equals()
is to prevent timing attacks. If you use a PHP version below PHP 5.6 the functionhash_equals()
is not supported. In this case you can replacehash_equals()
with the timingSafeCompare function:My solution is like this. It's not 100% bulletproof but I think it will save you for the most of the cases.
When user logged in successfully create a string with this information:
Encrypt
$data
, set type to HttpOnly and set cookie.When user come back to your site, Make this steps:
:
character.If user signouts, remove this cookie. Create new cookie if user re-logins.
Usually I do something like this:
Off course you can use different cookie names etc. also you can change the content of the cookie a bit, just make sure it isn't to easily created. You can for example also create a user_salt when the user is created and also put that in the cookie.
Also you could use sha1 instead of md5 (or pretty much any algorithm)
Generate a hash, maybe with a secret only you know, then store it in your DB so it can be associated with the user. Should work quite well.
Introduction
Your title “Keep Me Logged In” - the best approach make it difficult for me to know where to start because if you are looking at best approach then you would have to consideration the following :
Cookies
Cookies are vulnerable, Between common browser cookie-theft vulnerabilities and cross-site scripting attacks we must accept that cookies are not safe. To help improve security you must note that
php
setcookies
has additional functionality such asDefinitions
Simple Approach
A simple solution would be :
The above case study summarizes all example given on this page but they disadvantages is that
Better Solution
A better solution would be
Example Code
Class Used
Testing in Firefox & Chrome
Advantage
Disadvantage
Quick Fix
Multiple Cookie Approach
When an attacker is about to steal cookies the only focus it on a particular website or domain eg. example.com
But really you can authenticate a user from 2 different domains (example.com & fakeaddsite.com) and make it look like "Advert Cookie"
Some people might wonder how can you use 2 different cookies ? Well its possible, imagine
example.com = localhost
andfakeaddsite.com = 192.168.1.120
. If you inspect the cookies it would look like thisFrom the image above
192.168.1.120
HTTP_REFERER
REMOTE_ADDR
Advantage
Disadvantage
Improvement
ajax