Persistent Login Cookie Best Practice

January 19, 2004 11:52 PM

Update:

Here's a funny story.

About six years after I wrote this blog post, detailing an idea I had for managing persistent login cookies, a post that was linked to from all over the place, implemented in a couple of high profile libraries and that still gets me referrers (mostly these days from Stack Overflow. Hi!); after six years of being embarrassed that the products I worked on used a "remember me" mechanism I felt was demonstrably inferior, I finally got around to implementing the algorithm in real code.

It didn't work. It quite spectacularly didn't work. Because of concurrency.

I think there's a lesson here about the difference between theory and practice, and the hubris of a young blogger naming something "Best Practice" that he hadn't even tried yet.

And that concurrency is, and always will be, a bastard.

Original post:

Persistent login cookies are the cookies that are stored with your browser when you click the "remember me" button on the login form. I would like to be able to say that such cookies are obselete, and we have a better way of handling user logins, but they aren't, and we don't.

The following recipe for persistent cookies requires no crypto more powerful than a good random number generator.

Premises

  1. Cookies are vulnerable. Between common browser cookie-theft vulnerabilities and cross-site scripting attacks, we must accept that cookies are not safe
  2. Persistent login cookies are on their own sufficient authentication to access a website. They are the equivalent of both a valid username and password rolled into one
  3. Users reuse passwords. Hence, any login cookie from which you can recover the user's password holds significantly more potential for harm than one from which you can not
  4. Binding persistent cookies to a particular IP address makes them not particularly persistent in many common cases
  5. A user may wish to have persistent cookies on multiple web browsers on different machines simultaneously

Charles' Recipe

The cookie should consist of the user's username, followed by a separator character, followed by some large random number (128 bits seems mind-bogglingly large enough to be acceptable). The server keeps a table of number->username associations, which is looked up to verify the validity of the cookie. If the cookie supplies a random number and username that are mapped to each other in the table, the login is accepted.

At any time, a username may be mapped to several such numbers. Also, while incredibly unlikely, it does not matter if two usernames are mapped to the same random number.

A persistent cookie is good for a single login. When authentication is confirmed, the random number used to log in is invalidated and a brand new cookie assigned. Standard session-management handles the credentials for the life of the session, so the newly assigned cookie will not be checked until the next session (at which point it, too, will be invalidated after use).

The server need not make the effort of deliberately trying to avoid re-assigning random numbers that have been used before: the chance of it happening is so low that even if it did, nobody would know to make use of it.

When a user logs out through some deliberate logout function, their current cookie number is also invalidated. The user also has an option somewhere to clear all persistent logins being remembered by the system, just in case.

Periodically, the database is purged of associations older than a certain time-period (three months, perhaps: the size of the table would be far more an issue than any possibilities of collision in a 128 bit random space).

The following user functions must not be reachable through a cookie-based login, but only through the typing of a valid password:

  • Changing the user's password
  • Changing the user's email address (especially if email-based password recovery is used)
  • Any access to the user's address, payment details or financial information
  • Any ability to make a purchase

Results

If the login cookie is compromised, the attacker has access to the common functions of the site as that user. This is inevitable whatever the cookie contains. However, the attacker can not:

  • Access sensitive user information
  • Spend the user's money
  • Recover the user's password and try it on other sites
  • Prevent the user from receiving notifications from the site of things that may have been done in their name
  • Share the stolen login with others

The mutating nature of the cookie also provides a much smaller window of opportunity for an attacker to exploit a stolen cookie, and means the attacker must be far more careful they don't end up with a useless set of credentials.

Update: Barry Jaspan suggests an addition to the protocol that would further reduce the window of opportunity for stolen cookies: if a cookie that has been known to be used before (and thus invalidated) is presented, treat it as evidence of an attack and invalidate all saved logins for that user.

7 TrackBacks

Listed below are links to blogs that reference this entry: Persistent Login Cookie Best Practice.

TrackBack URL for this entry: http://fishbowl.pastiche.org/mt-tb.cgi/464

How blogging has helped me this morning from Raible Designs ~ We Build Web Apps on January 20, 2004 1:46 AM

My early-morning blog reading somes through once again. First off, Charles explains how I should implement Remember Me . If understand him correctly, I basically need to create a new table (i.e. user_cookie) that has two columns: username and cookie_id Read More

Charles has a nice post on Persistent Login Cookie Best Practice. I was about to work on this problem (and I know others were doing the same) so Charles' post is quite timely.... Read More

Cookie bandits from In dust we trust on January 20, 2004 12:30 PM

I don't like cookies. No big deal, I'm really not the only one, I think. For those looking up surprised, I am talking about those little things some websites drop on your pc... you didn't ask for it, they left it anyway, they don't apologize nor clean u Read More

From the Fishbowl comes a good summary of doing persistent login cookies in a way that is sane and logical.... Read More

"Remember Me" Best Practice from Dancing About Architecture on February 3, 2004 7:58 AM

Implementing a persistent login cookie is one of those annoying little tasks that you have to do when creating a web UI. I just wanted to say thank you to Charles Miller for documenting his best practice for handling that ... good job. Source: The Fish... Read More

TITLE: Cookies Overview: HttpCookie Class, usage and considerations URL: http://weblogs.asp.net/guys/archive/2004/11/05/252737.aspx IP: 66.129.67.203 BLOG NAME: guyS's WebLog DATE: 11/05/2004 07:26:27 PM Read More

Prateći neke blogove i mailing liste stekao sam utisak da je od početka ove godine u PHP zajednici ukazivanje na probleme sigurnosti u PHP skriptovima mnogo obimnije nego ranije. PHP je jezik koji dozvoljava "prljavo" kodiranje - jednostavnim pozivim... Read More

16 Comments

I imagine this could also be implemented using two cookies - one for the username, and the 2nd for the random number. Then add an additional column to your "user" table for storing the random number. This number (and cookie) would be updated each time the user logs in. Is this any less secure than your recommended strategy? I think it would be easier to implement (no new table or database logic required).

Instead of using a large random number, how about using something like: ONE_WAY_HASH_FUNCTION(username + SECRETS_STRING). No new database table is needed because you can verify the number on the fly.

Matt: It would work, but the advantage of Charles' solution is that it provides a "remember me" function from any number of PCs from which you have logged-in. To provide that you'd need a one-to-many mapping table between user and cookie in your existing db.

Ian: the purpose is that the random number is only valid once, the next time you visit the site.

Matt: You need multiple user->number associations so that someone can stay logged in from two different browsers. I don't want logging on to a site from work to log me off from home.

Ian: that would work, but you'd just lose the advantages of having a string of one-time authentication tokens.

I'm not entirely sure why the extra table is such a hassle. After all, it's only three columns (username, random id and age), and lookups will take no time whatsoever if you index it by the random number. I should write another article some day on the developer's instinctive fear of DBAs. :)

That is a neat idea, Charles. Thanks for sharing - you might have tried to patent it.

> The user also has an option somewhere to clear
> all persistent logins being remembered by the
> system, just in case.

An intuitive place for this option would be to do it whenever the user changes their password. Invalidating all auto-logins on password change mimics the behaviour of 'Remember Me' schemes that place the password into the cookie - the user must type the new password at their next browser session.

Correct me if I'm wrong, but even with this strategy, it still *is possible* for folks to steal this cookie (if it's a site like JRoller and you allow HTML comments). Although it's must less likely that they can login because the cookie is only valid once - it is possible, right?

Like I said in the article: "If the login cookie is compromised, the attacker has access to the common functions of the site as that user. This is inevitable whatever the cookie contains."

Protecting the integrity cookie is the first line of defense, and the most important one. Defense in depth, however, tells us that we must also make sure exposure is minimised in the event our first line of defense is breached.

Charles,
Am i correct in saying this is somewhat similiar to token pattern. Also instead of random number wont it be nice to use a date field. The advantage being it will be unique always and also if the user does not log on to a particular PC for quite some time say a week or so then the username need not be displayed. Please correct me if I am wrong.

Charles,

There is an "easy" solution to the random-number-collision problem (you dismiss it because it is so unlikely, but there is a solution, for completeness).

In the "database" where you are storing the mapping random->userid, add a unique primary key cookieid, and each row is now a tuple (cookieid, random, userid). (Obviously, use your DB's native "auto increment" primary key column type.)

Then, the cookie value is just cookieid:random:username.

=Matt

Matt: "auto-increment" implies that the magic number is predictable. Which is a bad thing.

Charles: I'm saying use a auto-increment as the primary key, but don't throw away that random! Basically, if you append something trully unique to something truly random (with a seperator) you end up with something truly random AND truly unique.

=Matt

I don't understand the point of a one time use cookie...

When I have seen one time use passwords in the past, I use a device that when I give it my password, it gives me a one time use password. When I log into the application with this password, it is no-longer valid. The thing is, I go to my device to get another one. In this situation the site gives me another one time use password. I don't think this adds any safety. Am I wrong?

Hi,

Isn't there still a problem with having a new randomly choosen cookie as an id?
- when the customer arrives onto the site, the previous cookie is invalidate and the new one is created.
- And as long as the customer goes onto the site it sends the newer cookie with the new key.
Someone can sniff the network to catch one good new key.

Am I missing something?

@Paul:

You are confusing two things. one-time passwords are passwords that are valid only once and indeed you have device (or paper) with the codes in/on them. Charles' scheme works with one-time tokens with which the user is not bothered. The cookie is fed with a token that is valid only the next visit and then renewed.

@Gabriel:

If user A (legitimately) enters your site, (s)he gets a new token. Should user B (maliciously) want to enter the site he could sniff for the cookie (and thus the token). He then has to use that token before user A enters (another page on) the site again. Suppose he succeeds - then user A will be logged out upon the next visit. He has to log in again (using the password) and thus creates a new string of one-time-tokens. User B can continue to impersonate A until the cookie expires.

Nicely said Frank, I just recently implemented the best practice by Charles into a website I am making. My cookie so far does not change on every page but on each entry into the site.

I was a little concerned about the theft of cookies and was thinking how about a IP check as well for each time cookie is used for login.
Is this over the top?

I would like to use as little system resources as possible but is this worth it?

Previously: Confluence Beta 2... er... 3

Next: Snippet