Live data from Hacker News

How to build (and how not to build) a secure “remember me” feature

troyhunt.com

21–30 of 65 posts

Re: How to build (and how not to build) a secure “remember me” feature

#21
post #18
post #8

Good article, but one thing is explained in a weird way: Don't bother with cookie expiration. That's the wrong approach, because the cookie is controlled by the user . Always do a server-side check whether or not the auth token in the cookie is allowed to continue the session. So you could simply set the expiration date for a cookie until 2030 but make sure that the auth token from that cookie cannot be used after $E…

Good point, but your explanation is also a bit confusing. If I understand correctly, your point is not so much "don't bother with cookie expiration" as "don't trust cookies to expire when you tell them to". In other words, the server should double-check cookie expiration dates because you don't want somebody fudging your 7-day cookie and using it to log in next year. Am I right?

Implement cookie expiration server-side.

Re: How to build (and how not to build) a secure “remember me” feature

#22
Remember me cookies are a specific case of some data sent to the client that the server would like to verify at a later point in time. The most direct way that comes to mind for solving this type of problem is to send the client just a random identifier and have the server look it up in a persistent store upon use.

The other way of solving it is to have the server sign whatever data is sent to the client via an HMAC. That combined with some basic serialization gets you a generic approach that you can use for all kinds of things. This also has the scalable benefit that it doesn't require a centralize persistent store.

Here's a high level summary of how we handle these use cases:

# Server -> Client (Assume server wants to round trip object X)

  1. Server serializes X to a URL friendly string (JSON + Base64 works well enough for this)
  2. Server calls generateToken(type, message, expiration) which signs the message/expiration with secretKey+type. It then returns a JSON/Base64 serialized map containing message/expiration/hmac.
# Client -> Server

  1. Client sends back the final JSON/Base64 string
  2. Server decodes and extracts the message, expiration, and hmac
  3. Server verifies the HMAC by regenerating it and comparing it against the client supplied one
  4. Server verifies the expiration date hasn't passed
  5. Server returns back the deserialized object
Couple of notes:

* The HMAC prevents the client from tampering with the message

* The 'type' field is so that tokens generated for one request type cannot be accepted somewhere else in the application. It's kind of like namespacing.

* The 'type' field does not need to be included in the message itself. It's inferred by the server based on the client request type.

* The object/message itself can be blank. In that case this becomes a secure expiring token.

Couple of possible extensions/improvements:

* If the data being sent back/forth is particularly sensitive you could also have the server encrypt either it or the entire message itself. If it's not though then it would be overkill.

* Including the requesting clients IP in the HMAC generation to further limit the set of user's it would validate against is an option as well though generally that's a bad idea. People's IP addresses change fairly often and that kills the basic use case of taking your work home with you.

Re: How to build (and how not to build) a secure “remember me” feature

#23
post #20

Earlier quoted context omitted.

One significant benefit is being able to enforce a one-time use policy the remember-me cookie. If you only utilize it when the user is not logged in, then you use it to authenticate, set a regular session cookie, and generate a new remember-me cookie. If an old remember-me cookie is ever used that means someone probably sniffed the cookie, and you can invalidate all sessions at that point and even force a password re…

> You can't do this with the regular session cookie because a user might have multiple tabs open. Why not? It's considered standard practice to refresh regular session identifiers every X minutes, and this rarely causes race conditions unless your app is AJAX-heavy. My apps regenerate the session identifier every time it detects that it has been more than 5 minutes since the last regeneration. So if a user who has be…

If an attacker uses a compromised session id before it is regenerated, the attacker will receive the regenerated session too. They'll have a long-lived session to the victims account.

Re: How to build (and how not to build) a secure “remember me” feature

#24
post #22

Remember me cookies are a specific case of some data sent to the client that the server would like to verify at a later point in time. The most direct way that comes to mind for solving this type of problem is to send the client just a random identifier and have the server look it up in a persistent store upon use. The other way of solving it is to have the server sign whatever data is sent to the client via an HMAC.…

This is a good approach for a lot of things. It's the approach Flask uses for its session data by default. I wouldn't recommend it for authentication in large deployments though.

One thing that it doesn't allow for is revocation of individual tokens. Invalidating auth tokens centrally is a very useful capability to have.

Re: How to build (and how not to build) a secure “remember me” feature

#25
post #22

Remember me cookies are a specific case of some data sent to the client that the server would like to verify at a later point in time. The most direct way that comes to mind for solving this type of problem is to send the client just a random identifier and have the server look it up in a persistent store upon use. The other way of solving it is to have the server sign whatever data is sent to the client via an HMAC.…

> The most direct way that comes to mind for solving this type of problem is to send the client just a random identifier and have the server look it up in a persistent store upon use.

This is my preferred approach. I generate random 128-bit integers (using a random number generator who's purpose is to be used for cryptography purposes, so it's hard to observe) and store these in Redis with a year expiry, and send them back to the client to store in a cookie. When this token in the cookie is used, the TTL on the token is reduced to 5 minutes, which lets power users up multiple tabs at once. It's the simplest, but also most robust way, I've found to deal with this.

The cleverer I try and be, the more I tend to mess these things up.

Re: How to build (and how not to build) a secure “remember me” feature

#26
post #22

Remember me cookies are a specific case of some data sent to the client that the server would like to verify at a later point in time. The most direct way that comes to mind for solving this type of problem is to send the client just a random identifier and have the server look it up in a persistent store upon use. The other way of solving it is to have the server sign whatever data is sent to the client via an HMAC.…

It's also possible to make the cookies non-exportable using a similar technique called channel binding[1], where the cookie is linked to the TLS channel it's minted over.

This is a lot more powerful than baking in the IP address when HMAC-ing the cookie but requires modification to the browser and server to get it up and running.

[1] http://www.browserauth.net/channel-bound-cookies

Re: How to build (and how not to build) a secure “remember me” feature

#27
post #22

Remember me cookies are a specific case of some data sent to the client that the server would like to verify at a later point in time. The most direct way that comes to mind for solving this type of problem is to send the client just a random identifier and have the server look it up in a persistent store upon use. The other way of solving it is to have the server sign whatever data is sent to the client via an HMAC.…

This is a good approach for a lot of things. It's the approach Flask uses for its session data by default. I wouldn't recommend it for authentication in large deployments though. One thing that it doesn't allow for is revocation of individual tokens. Invalidating auth tokens centrally is a very useful capability to have.

Yes that's the biggest downfall and also where having a centralized persistent store wins out. The best compromise between the two I've thought of is to have a centralized store that lists revoked tokens.

Presumably there wouldn't be that many so storing the recovations alone would be more efficient. Bonus points if you add a Bloom Filter[1] in front of the revocation lookup.

In the end it's all about the use case you're solving. If the token's themselves already have a short expiration (ex: password reset token with 5 minutes to live) then revocation isn't really an issue. For something long lived and dangerous (ex: remember me token to log into my bank account) it's much more important.

[1]: http://en.wikipedia.org/wiki/Bloom_filter

Re: How to build (and how not to build) a secure “remember me” feature

#28
post #20

Earlier quoted context omitted.

> You can't do this with the regular session cookie because a user might have multiple tabs open. Why not? It's considered standard practice to refresh regular session identifiers every X minutes, and this rarely causes race conditions unless your app is AJAX-heavy. My apps regenerate the session identifier every time it detects that it has been more than 5 minutes since the last regeneration. So if a user who has be…

If an attacker uses a compromised session id before it is regenerated, the attacker will receive the regenerated session too. They'll have a long-lived session to the victims account.

If the attacker uses a compromised remember-me cookie, it will also be regenerated for him. Same problem.

Re: How to build (and how not to build) a secure “remember me” feature

#29

Regarding how long to keep the "remember me" cookie for, this is one of the overlooked reasons why native apps are eating the web's breakfast. When was the last time you have to re-authenticate on a native mobile app? Maybe it happens on some finance/banking apps, but I don't recall seeing it on apps like Facebook or Kindle for example. Web developers could, for example, add longer times if the user agent is mobile.…

> When was the last time you have to re-authenticate on a native mobile app? All the time. The Facebook android app is horrible.

Not to refute your experience, but I've never had to re-auth facebook on my android.

Re: How to build (and how not to build) a secure “remember me” feature

#30
post #27

Earlier quoted context omitted.

This is a good approach for a lot of things. It's the approach Flask uses for its session data by default. I wouldn't recommend it for authentication in large deployments though. One thing that it doesn't allow for is revocation of individual tokens. Invalidating auth tokens centrally is a very useful capability to have.

Yes that's the biggest downfall and also where having a centralized persistent store wins out. The best compromise between the two I've thought of is to have a centralized store that lists revoked tokens. Presumably there wouldn't be that many so storing the recovations alone would be more efficient. Bonus points if you add a Bloom Filter[1] in front of the revocation lookup. In the end it's all about the use case yo…

Yes, a password reset token is a good example where it would be fine to used a HMACed token, and is exactly how Django handles password resets [1].

[1] https://github.com/django/django/blob/master/django/contrib/...

Post reply on HN