548

I want my website to have a checkbox that users can click so that they will not have to log in each time they visit my website. I know I will need to store a cookie on their computer to implement this, but what should be contained in that cookie?

Also, are there common mistakes to watch out for to keep this cookie from presenting a security vulnerability, which could be avoided while still giving the 'remember me' functionality?

reformed
  • 4,505
  • 11
  • 62
  • 88
Eddie Deyo
  • 5,200
  • 8
  • 35
  • 35
  • 5
    Check http://stackoverflow.com/questions/549/the-definitive-guide-to-forms-based-website-authentication (part II of top answer) – Maxime Pacary Dec 14 '11 at 13:29
  • if you are using ASP.NET, check out http://www.codeproject.com/Articles/779844/Remember-Me – Believe2014 May 30 '14 at 18:07
  • 1
    There is some very useful info over in Security SE ~ http://security.stackexchange.com/questions/19676/token-based-authentication-securing-the-token – arkon Aug 01 '15 at 10:28
  • 1
    The currently accepted answer by splattne is overly complex. Create a +16 byte token from a random source, hash it, and save the hash + account id in the database. Then send the token to the user (base64 encoded) in a HTTPS + httpOnly cookie (so Javascript can't access/steal it). This way, no one can guess the token or log people out with invalid guesses, yet even if your database is hacked no one can use the tokens in the database (they are hashed). So only the original client (or someone who steals the token from the browser store somehow) can use it. – Xeoncross Jan 02 '20 at 22:48

4 Answers4

572

Improved Persistent Login Cookie Best Practice

You could use this strategy described here as best practice (2006) or an updated strategy described here (2015):

  1. When the user successfully logs in with Remember Me checked, a login cookie is issued in addition to the standard session management cookie.
  2. The login cookie contains a series identifier and a token. The series and token are unguessable random numbers from a suitably large space. Both are stored together in a database table, the token is hashed (sha256 is fine).
  3. When a non-logged-in user visits the site and presents a login cookie, the series identifier is looked up in the database.
    1. If the series identifier is present and the hash of the token matches the hash for that series identifier, the user is considered authenticated. A new token is generated, a new hash for the token is stored over the old record, and a new login cookie is issued to the user (it's okay to re-use the series identifier).
    2. If the series is present but the token does not match, a theft is assumed. The user receives a strongly worded warning and all of the user's remembered sessions are deleted.
    3. If the username and series are not present, the login cookie is ignored.

This approach provides defense-in-depth. If someone manages to leak the database table, it does not give an attacker an open door for impersonating users.

3limin4t0r
  • 19,353
  • 2
  • 31
  • 52
splattne
  • 102,760
  • 52
  • 202
  • 249
  • 21
    see also:http://stackoverflow.com/questions/549/the-definitive-guide-to-website-authentication-beta#477579 you should NOT read the 'improved' version – Jacco Mar 05 '09 at 11:56
  • 9
    The problem with this is that you expose the username in the cookie, though this is what Gmail does. Why do you need both a series ID and a token? Wouldn't a bigger token be fine? – Dan Rosenstark Jul 02 '09 at 15:16
  • @yar: no it wouldn't be fine and the article describes why – cherouvim Apr 14 '10 at 13:17
  • @yar: the article describes in detail. the series is being used to mitigate the results of a cookie theft. – cherouvim Apr 15 '10 at 07:06
  • 4
    Also make sure that changing the user's password or password recovery information requires the original password. If the cookie is compromised, the attacker cannot prevent the original owner from accessing the account. – Siqi Lin Aug 25 '10 at 03:43
  • 2
    can anyone explain when will the series identifier change? if never, then storing the username encrypted and the token will suffice? – Valentin Brasso Oct 31 '10 at 00:19
  • 2
    the whole concept of the series identifier does not make sense as it is described here. –  Sep 19 '11 at 04:47
  • 4
    @altvali A new series identifier is issued when a valid username/password combination is presented to authenticate the user (instead of a token). This starts a new series of random tokens used to identify this particular (long) login session. – tvanfosson Dec 14 '11 at 23:26
  • 2
    @tvanfosson...why would you need this? Just generate a new UNIQUE token...there is no reason a single user could not have mutliple tokens...one for each login....Token A, Token B, Token C...there is no need for a series identifier. –  Feb 07 '12 at 14:21
  • 11
    Also, regarding this model, what it to prevent an attacker from stealing and than placing the cookie on his computer and deleting the cookie from the hacked computer. His computer would than be authenticated and updated as needed with out the hacked computer ever knowing? The only change would be that the hacked computers user would have to login again and set remember me. Whether or not the hacked user recognizes this would be uncertain. –  Feb 07 '12 at 14:23
  • 1
    You would need to add in computer characteristics..for uniqueness either a MAC address or a set of other characteristics like user agent, screen resolution, etc. –  Feb 07 '12 at 14:48
  • Isn't is more stable to use a ID instead of a username so you don't have to worry about any case sensitive and all the other problems that come with strings? – Daniël Voogsgerd Apr 21 '12 at 17:02
  • 2
    I've posted a question about this answer: http://stackoverflow.com/questions/10957584/stay-logged-in-best-practices-how-does-a-username-in-the-cookie-make-it-more-se – Chris Moschini Jun 09 '12 at 00:58
  • 26
    @HiroProtagonist The Series Identifier is to prevent a DoS attack. Without it, I could quickly write a script hitting your site with every username and an invalid token, logging everyone on your site out. – Chris Moschini Jun 09 '12 at 01:01
  • 2
    Why not add a hash of the username instead? – Eran Medan Jul 15 '12 at 23:25
  • If a new token is only generated in step 3.1, which only happens for a non-logged in user, wouldn't the new token be sent with every request of a logged in user? This leaves a lot of chances for a packet to be intercepted. Could this cookie be configured to only send when not logged in? – kingcoyote Oct 31 '12 at 21:14
  • @kingcoyote set the path of the cookie to the path of the auto login page – Jesse Nov 13 '12 at 15:57
  • 3
    I implemented this solution yesterday and I think it works well, something that isn't mentioned is how to deal with 'orphaned' authentication informationin the DB. When the user manually clears his cookie cache, the authentication info in the database is orphaned. Perhaps implementing an auto update timestamp in the table and manually removing all authentication data over a certain age would be a good idea. – paj Mar 13 '13 at 09:58
  • @ChrisMoschini Been awhile but I hope it's okay. Could you explain why a series identifier helps prevent Dos attack? Like HiroProtagonist, I don't see much difference between using a longer token and series identifier + token. – roverred Aug 08 '13 at 00:53
  • 6
    @roverred If you just use a long token, and destroy all sessions for a user when any invalid token arrives, I can write a script that hits your site with every username and all 0's for the token to log all users out. Run it in a loop, no one can ever meaningfully login. If a valid Series token is required however before the server acts against sessions, the DoS attack is prevented. – Chris Moschini Aug 08 '13 at 05:17
  • 2
    @Yar the email id is included to maintain the uniqueness, since it can happen that the same token is generated for two different accounts – Lakshay Dulani Apr 21 '14 at 14:41
  • @ChrisMoschini: Why would they be logged out? I only see that "remembered sessions are deleted", meaning that you could effectively DoS the rememberme feature but not the whole authentication, right? – Levente Pánczél Jul 09 '14 at 10:20
  • Improved version actually does not provide an extra level of security, only an illusion of it.As @Jens Roland claims, pretty much any attack vector that allows the user to steal the cookie also allows it to be deleted from the hacked computer. – Onur Aug 11 '14 at 01:21
  • Could you provide a simpler version with only **username + token** just in order to understand the basics ? (Then I'll be able to add the *series*) – Basj Oct 07 '14 at 23:52
  • 6
    If the user has a poor internet connection could this be an issue when receiving the new token? Example, user requests login with token, internet connection drops out, new token generated at server, new token not received by user, user tries again with old token, this is seen as a hack attempt and all tokens are removed from db, user now has to login again – Joel Davis Nov 25 '14 at 02:32
  • 4
    Chrome sends a loading request when you type the URL in the address bar. This can cause the remember me code to execute prematurely which will invalidate the token by the time the real request is executed when the user presses enter in the address bar. I didn't see an easy way around this problem except to use a static cookie that doesn't change during subsequent logins. Anyone else encounter this problem? – craigrs84 Feb 05 '15 at 05:28
  • 8
    This solution is WRONG, it does not handle cuncurrency: If two remember-me authentication request arrives at the same time, with the same remember-me cookie, the first one succeeds and changes the token, the second one causes an unsucceesfull authentication, and a false alarm (because the token has been already changed by the first request). (This situation can happen when the browser starts up, and the site is restored in two browser tabs.) – slobo May 14 '15 at 10:54
  • 2
    In this case, race conditions are less important than preventing replay attacks. Race condition: Have to manually log back in. Replay attack: Tokens become more desirable to steal, since they are not a nonce. – Scott Arciszewski Jun 07 '15 at 19:54
  • 2
    Presenting a false alarm to the user is very undesirable. Whether it is because of reasons mentioned by slobo, craigrs84, or Frogga, this solution generates these false alarms routinely. – Mike C Oct 28 '15 at 05:14
  • 3
    This question and answer are very weird. It's not mentioned if user id should be stored on a client and sent together with series and token for validation. So reader has to guess that series is probably a replacement for user. Second problem, question is closed, so only source of info are comments. Third problem: concurrency. And it's not small because opening same site in 2 tabs at once is very frequent. And you would not want it to fail even 1% of the time. – clorz Nov 16 '15 at 23:39
  • @Frogga one potential fix would be to store 2 tokens (previous and current) with each session in the db. If previous is received, that means current was not received and a new current is generated and returned. If current is received, current becomes previous and a new current is generated and returned. This however does not fix the above mentioned concurrency issues. – Emanuel George Hategan Jan 06 '16 at 11:12
  • 1
    *"A new token is generated"* ..! Why? Why every time should I create a new cookie? – stack Jun 27 '16 at 17:45
  • 1
    Here's a golang implementation: https://github.com/janekolszak/rememberme – Janek Olszak Sep 03 '16 at 19:19
  • The answer says "If the series identifier is present and the hash of the token matches the hash for that series identifier, the user is considered authenticated. " The series identifier isn't hashed so this makes no sense. I think what he's trying to say is "If the series identifier is present in the cookie, and the hash of the token in the cookie matches the hash of the token stored in the table alongside that series identifier, the user is considered authenticated. " – jim collins Nov 08 '16 at 17:03
  • As understand need to create hash for cookie name and value. Both write in mysql. At login page check if cookie with particular name (hash) exists. If yes, then select mysql where cookie name and value is the same as in mysql. If yes, then from mysql get user id and set sessions that user is logged in and id is mysql id. Am i correct? As understand cookie identifier is cookie name? – Andris Jan 15 '17 at 14:43
  • 1
    I am wondering why would I need 2 random numers insteed of single token? Is someone says SECURITY, then I would say, heck lets use 100 random tokents then. – Antoniossss Jun 02 '17 at 06:36
  • You guys should read this article [link](http://jaspan.com/improved_persistent_login_cookie_best_practice), it tells us if a hacker accesses a victim's account, then the hacker has T_1, the victim has T_0, and when the victim access his account, he can know that someone accessed his account because he has T_0 (the unvalid token and the valid series). – Tai Le Jul 19 '17 at 04:02
  • Yo made the updates 444 :D – d_void Oct 27 '17 at 19:46
  • 1
    "If someone manages to leak the database table, it does not give an attacker an open door for impersonating users" It does. But just until the user uses the site again. If someone steals series identifier and a token while the user is not accessing the site then it works like a charm. – drinovc Feb 23 '18 at 13:19
  • 1
    @slobo I was going to ask exactly the same question. You are totally right, if the user has two tabs open, the will get logged out from both tabs (even if he was authenticated in the second). I suggest that the token gets updated on every login or logout and not on every call. – Rami Zebian Sep 29 '18 at 16:20
  • 2
    @drinovc it won't work though because when the user gets access to the database table, all they will get is the series and the hashed token. So if they created two cookies, one for each, the token will always fail because they've got a hashed version of the original – A Friend Dec 10 '18 at 23:17
  • 2
    @ChrisMoschini how does the series prevent a DOS attack? What's to stop me from writting a script that tries all variations of the series cookie with the token just set to '1', it'll go through and log everyone out – A Friend Dec 10 '18 at 23:25
  • @drinovc Not if you simply treat the RememberMe token as a password and only store its HMAC. – nphx Mar 19 '19 at 15:38
  • Why not just IP based rate limiting? I get that `hash_equals()` will prevent timing leakage (which would take millions of requests to filter out network latency en server activity) but how is a 12char series lookup going to better protect you from DoS than a 64char token lookup? – dehart Aug 19 '21 at 07:50
  • Just in case others were a little confused by @Jacco's comment - "you should NOT read the 'improved' version", here is my synthesis. I implemented the "updated strategy described here (2015)" but found that there were frequent false alarms about cookie theft (mentioned by other comments above). These happen due to concurrent requests (multiple tabs opening at once), poor internet (new cookie not updating), and Chrome's load pre-request. The changing token after each request is the problem. Just follow https://paragonie.com/blog/2015/04/secure-authentication-php-with-long-term-persistence. – LStarky Oct 09 '21 at 01:52
11

Store their UserId and a RememberMeToken. When they login with remember me checked generate a new RememberMeToken (which invalidate any other machines which are marked are remember me).

When they return look them up by the remember me token and make sure the UserId matches.

jonnii
  • 28,019
  • 8
  • 80
  • 108
  • This can be brute forced in seconds. I'll just set my user_id to 1 and brute force all tokens. It'll give me access in seconds – A Friend Dec 10 '18 at 23:21
10

I would store a user ID and a token. When the user comes back to the site, compare those two pieces of information against something persistent like a database entry.

As for security, just don't put anything in there that will allow someone to modify the cookie to gain extra benefits. For example, don't store their user groups or their password. Anything that can be modified that would circumvent your security should not be stored in the cookie.

reformed
  • 4,505
  • 11
  • 62
  • 88
dragonmantank
  • 15,243
  • 20
  • 84
  • 92
5

Investigating persistent sessions myself I have found that it's simply not worth the security risk. Use it if you absolutely have to, but you should consider such a session only weakly authenticated and force a new login for anything that could be of value to an attacker.

The reason being of course is that your cookies containing your persistent session are so easily stolen.

4 ways to steal your cookies (from a comment by Jens Roland on the page @splattne based his answer on):

  1. By intercepting it over an unsecure line (packet sniffing / session hijacking)
  2. By directly accessing the user's browser (via either malware or physical access to the box)
  3. By reading it from the server database (probably SQL Injection, but could be anything)
  4. By an XSS hack (or similar client-side exploit)
reformed
  • 4,505
  • 11
  • 62
  • 88
  • 113
    1. HTTPS is designed to prevent this. 2. Stay Logged In isn't the security problem here, you have bigger problems. 3. Same as 2. 4. This can be prevented by access-control policy and good input sanitation; if you don't take these steps, you again have bigger problems than Stay Logged In. – Chris Moschini Jun 08 '12 at 18:45