Live data from Hacker News

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

troyhunt.com

41–50 of 65 posts

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

#41
post #7
post #6

I tend to like doing: sha256(sha256($password) . $ip) This encrypts the password and makes the cookie only usable by the IP it was set for. Then to verify the cookie, since I already store sha256 of the password, it's trivial to do, without having to store an additional token for persistence. Of course you can replace sha256 with your fav hashing function.

What if the user gets their session hijacked when they're on a static or long-term dynamic IP? If the user were to relogin, their session ID would be the same. I think it would be beneficial to at least store a salt associated with their session and regenerate the salt when authenticating. sha256($password . $ip . $random_salt) You could also regenerate the salt periodically.

No. None of these. Please stop. The system is entirely broken. No amount of tweaking is going to fix it.

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

#42
post #17

Can someone explain how the example of the JSON encoded cookie could be used for XSS?

I don't know if it's what Troy was implying, but JSON as a cookie payload along with the seemingly ubiquitous "parsing JSON using YAML and the YAML parser executing it as arbitrary code" vulnerability seems like it'd at least be worth attempting to attack there.

(Probably not what Troy was thinking, since that'd result in server-side SQLi or arbitrary code execution problems, not client-side ones like XSS. Also, I thnik Troy is more a .Net/asp kind of guy, not a Ruby-ist - which might make that YAML parser issue in Ruby/Rails not something he's concerned about)

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

#43
There's a lot of misinformation in this thread, so I'm going to describe best practice for this sort of thing.

  1. The client authenticates, and asks to be remembered.
  2. The server generates a cryptographically random token of at least 128 bits in length. This token is *never* directly stored server-side.
  3. The hash (or, possibly, a slow-hash) of the token is saved in the database along with a time of expiry
  4. The non-hashed token is sent to the client as a cookie completely independent of the session cookie
When the client visits the website:

  1. The server checks for the presence of the remember-me cookie
  2. If the cookie is set, it is hashed and this hash is searched for in the database (and filtered to tokens with expiry times after now)
  3. If the user is successfully logged in, the old token is deleted from the database. A new token is generated and sent to the client by the previously-described process
Now for a little bit of explanation.

This process completely isolates the authentication token from the session. If a token is somehow intercepted or discovered by an attacker, it is only usable for a single session. Such tokens should never be allowed to serve as permanent entry points into a user's account.

The token is never directly stored server-side. This confers two benefits. First, these tokens should be considered password-equivalent — if someone manages to steal your table of remember-me tokens, they would be completely unable to use them to log into a user's account. Second, this prevents timing attacks; if you simply looked for matching tokens in your database, an attacker can (shockingly simply) use timing information to guess a user's remember-me token in O(n) time.

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

#44
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…

I wouldn't go as far as "Don't bother with cookie expiration.", but you're certainly right in that you can't _rely_ on "the browser" to honor them (or even actually be a browser).

I'd still recommend setting reasonable expirations, even if it's only to be seen to be doing the right thing. Far future expirations aren't useful (as you explain) and they only serve to make it look like you're "doing in wrong". (And, for the 99.9% case of non-malicious regular users, expiring the cookies normally saves the sever the effort of looking up the session state of an already expired session with a 2030 expiry cookie. Don't _rely_ on it, but take advantage of it working right under normal conditions.)

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

#45
post #28

Earlier quoted context omitted.

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

If you use the scheme described linked from the article, when the legitimate user logs in again, the attacker will lose access to the session permanently.

Oh, I see. A separate cookie makes it easier for you to check for compromised sessions. I suppose you could also do that with regular session cookies if you keep good track of identifier history, but it'd be a lot more hassle.

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

#46

Remembering the username can be pretty useful, especially if you find yourself in a place where you common usernames are taken on other sites and you have to use different usernames in different places.

Not to mention the sheer convenience of one less thing to type--especially helpful if you are on a mobile device, where typing is still a pain vs. on a full keyboard.

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

#47
post #6

I tend to like doing: sha256(sha256($password) . $ip) This encrypts the password and makes the cookie only usable by the IP it was set for. Then to verify the cookie, since I already store sha256 of the password, it's trivial to do, without having to store an additional token for persistence. Of course you can replace sha256 with your fav hashing function.

I'm not sure why you'd ever want to put something password-derived in a cookie--even if its sha256'd 8 dozen times, then scrypted, etc.

There's just no value in using the password as a basis. Why not instead just generate a random, unique token?

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

#48
post #21
post #18

Earlier quoted context omitted.

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.

Would anyone be tempted to do this "client-side"? What would that even mean, besides what the browser does automatically? Or are we just saying, "don't trust that the client won't send expired cookies"?

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

#49
post #43

There's a lot of misinformation in this thread, so I'm going to describe best practice for this sort of thing. 1. The client authenticates, and asks to be remembered. 2. The server generates a cryptographically random token of at least 128 bits in length. This token is *never* directly stored server-side. 3. The hash (or, possibly, a slow-hash) of the token is saved in the database along with a time of expiry 4. The…

I don't see why you have to store the hash of the token. You know the key you used for HMACing, why can't you just check that the cookie contains a valid hash of the rest of its data (which is as extensive as you need: session id, expiry, IP, whatever)? To prevent the use of old sessions just make the session id a counter. A single session counter per user is probably less hassle (and more useful) to store than the whole hash.

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

#50
post #43

There's a lot of misinformation in this thread, so I'm going to describe best practice for this sort of thing. 1. The client authenticates, and asks to be remembered. 2. The server generates a cryptographically random token of at least 128 bits in length. This token is *never* directly stored server-side. 3. The hash (or, possibly, a slow-hash) of the token is saved in the database along with a time of expiry 4. The…

I don't see why you have to store the hash of the token. You know the key you used for HMACing, why can't you just check that the cookie contains a valid hash of the rest of its data (which is as extensive as you need: session id, expiry, IP, whatever)? To prevent the use of old sessions just make the session id a counter. A single session counter per user is probably less hassle (and more useful) to store than the w…

To avoid the problem of storing the token's hash, you have introduced:

  * parsing a structured cookie, vs a meaningless 128-bit string
  * securely storing and managing a secret HMAC key (which may be used to forge or modify credentials)
  * securely verifying an HMAC using a constant-time string comparision
So what, exactly, is the benefit of your proposal over mine? You've removed the (useful) distinction between an authentication token and the user's session. You've introduced significant additional complication and moving components. And you've increased the attack surface for security vulnerabilities. For what?

Doing all that to store a 32-bit integer is somehow less hassle than hashing and storing a 128-bit string?

Post reply on HN