Best practice for remember me feature [duplicate]
Asked Answered
B

5

9

I am using 2 variables in cookie (7 day expiration) which is user id and hash. Hash is sha1 encode of user agent and user id. In this case some hacker can login who is know stolen cookie's browser. Which way should I follow or which practice is best for remember me security problems?

Balcke answered 20/8, 2010 at 13:42 Comment(2)
Related: stackoverflow.com/search?q=php+session+hijackingLifegiving
You should re-use an existing authentication framework whenever possible, because, really, it's complex. For example, take a look at github.com/delight-im/PHP-AuthDebris
B
6

While you can hash a user_id and secret_key, anyone who intercepts this cookie can log in to your application. In addition to this, you can make it so that your remember me cookies go stale very quickly. No one likes a stale cookie.

You can store the time stamp of each user's last visit in your database and in the cookie. Each time you read the cookie to log the user in, you check to see that both timestamps match. If they don't, deny the user. If they do, update the timestamps.

Using this method, any time your user returns to your site, all old cookies go stale. A hacker that has intercepted a cookie now has a worthless stale cookie because he does not know the exact time stamp in the current cookie. Of course, the hacker can use a fresh cookie as much as he wants until the user logs back in.

//check for cookie
if(isset($_COOKIE['remember_me'])) {
   // get hash and time stamp from cookie
   $hash = substr($_COOKIE['remember_me'],0,40);
   $last_visit = substr($_COOKIE['remember_me'],41);

   // query your db with $hash and $last_visit

   // if hash and time stamp match up
      // log in

      // store the current time stamp in a variable to use for both
      $time = date("Y-m-d H:i:s");
      // update the time stamp in your cookie
      $cookie = $pass . "-" . $time;
      setcookie('remember_me', $cookie, time()+60*60*24*100, '/');
      // update the time_stamp in your database
   else {
      // remove the remember me cookie
      setcookie('remember_me', '', time()-42000, '/')
   }

This method offers a small amount of security, and should certainly be used along side methods proposed in other answers. A hashed key should be stored in the cookie. A remember me cookie cannot be perfectly secure, so password re-entry should be required for any additional access to highly sensitive data or application features.

I also recommend naming your cookie something besides 'remember_me' to make it a little harder to find. While it does not add much security, if any, naming your cookie 'ht33424' takes just as long as naming it 'remember_me' or 'hack_me'.

Blackfish answered 20/8, 2010 at 14:11 Comment(4)
Sorry but I don't think I like this answer, unless I misunderstand the logic of it this relies on the user loading a new page before the hacker has time to copy and execute the page on their side. At the end of a genuine users session there is a big window for hijacking, and even during a session it shouldn't be too hard to slip in between page loads. This would create unusual page behaviour for the genuine user. Renaming cookie variable names is security through obscurity and a waste of time. (Continued)Apish
... It is probably another layer that will help, but a determined hacker will not be detered by it. Also it is not 100% failsafe for the reasons I mentioned above. For sensitive and potentially damaging access to sensitive information and functionality I would not recommend this method as a 100% secure solution, only a marginal improvement.Apish
To a determined hacker, no method is 100% failsafe. As you said, this really is just one more layer to try and get us closer to 100%. This is meant to be used along side other methods, many of which are provided in other answers. A hashed key should definitely be stored in the cookie, and password re-entry should be required for additional access to sensitive information and features.Blackfish
Without this method, a hacker could still slip in between page loads, creating unusual page behavior for the genuine user. This method forces a hacker to re-intercept a cookie every time the genuine user logs in. Since this may not be a problem for a determined hacker, this method only adds a small level of security to other methods discussed here.Blackfish
A
1

You can simlply set the expiry date as now plus a year on the cookie, but then have an enter password field in all sensitive areas, much like the implementation amazon uses. A hijacked cookie will grant access but to purchase or modify anything personal requires password to be re-entered.

The problem with 'remember me' tables is that if a hacker can gain access to this table he can create and login to as many accounts as he wants. You can argue it strengthens security of a remember me feature, but it needs to be weighed in with the risks of softening knee areas of security.

Apish answered 20/8, 2010 at 14:40 Comment(0)
L
0

Personally, I create a random hash and store it in a "remember me" table. The table also has the user agent, user id and the IP address. I check both every time I re-login the user from the remember-me function. And if the user manually logs out, I simply remove that row from the table. Then if they login again, it creates a new random hash. There's really no way to combat someone sniffing packets with a remember me system (unless you use secure cookies with the HTTPS only flag set). So use a secure connection with HTTPS-only cookies. If you can't, then at least make the the hash random so if it is discovered you can at least generate a new hash to kill that login...

Lighter answered 20/8, 2010 at 13:50 Comment(3)
Wasn't me, but I am always wary of implementation like this as it assumes variables such as user agent and ip are constants when they are often not. I wouldn't define this as a solution but more of a workaroundApish
Fair enough. But in the absense of a HTTPS only cookie, are there really any better ways of attempting to verify the source?Lighter
I haven't seen or heard of one yet unfortunatly, I think if you want a remember me feature it should be only to access low importance areas as a misused remember me feature has a myraid of security concerns such as shared computers etc. On phone so appologise for grAmmar.Apish
C
0

You can eventually use sessions to store user status. But holding session so long cause same problem, when session id will be stolen. You may join cookie informations with some other - like browser or IP address, but it would cause a problem, when user don't have static IP.

Anyway, holding a session id is safer that just putting user's sha1 endoded password to cookies.

Chaney answered 20/8, 2010 at 13:50 Comment(2)
Fortunately, the OP didn't talk about password at all :) But yes, a session ID might be useful here.Ungotten
Long running sessions are almost always a bad idea. They open the door to specific DOS attacks since you can eat up a non-insignificant amount of storage space since PHP can't GC the sessions until the timelimit is reached. There are some instances where it's necessary, but for the most part it's not a good idea...Lighter
H
0

HMAC

I usually do this way so I've nothing to store on the server side on databases or similar.

You have to generate random string that becomes your "secret key" and that you have to store on the server side (probably in a config php script as a constant) and you have never to tell to anyone. I'll call this secret key SECRET_KEY.

Then your cookie has to set two values:

  • USER_ID: The user id of the user which will get the automatic login
  • HASH: A secure cryptographic hash of USER_ID and SECRET_KEY. So, for example, md5(USER_ID . "-" . SECRET_KEY). (Something different than md5, such as sha1 or sha256 is preferred).

So your final cookie could be: USER_ID:HASH.

Then, when you have to check if a cookie is a genuine remember me cookie, you have to do so:

function isCookieGenuine($cookie_value) {
  list($value, $hash) = explode(':', $cookie_value, 2);

  if (md5($value . "-" . SECRET_KEY) == $hash)
    return true;
  else
    return false;
}

The point is that only you can generate an hash that passes this check because the hash needs not only the USER_ID but also the SECRET_KEY that is unknown by anyone other than the server! :)

As noted in the comments you can do this by using the hash_hmac function in PHP >= 5.1.2: http://us.php.net/manual/en/function.hash-hmac.php

Hartung answered 20/8, 2010 at 13:52 Comment(2)
Too bad array dereferencing doesn't work in PHP yet (it's implemented in trunk only). so you can instead do list($value, $hash) = explode(':', $cookie_value, 2);. And it may be better for security if you implement a different "secret" for each user (since then a one time compromise of a secret doesn't open the door for every user). Oh, and PHP has a built in hash_hmac function to do this very thing...Lighter
@ircmaxell, +1 for your comment (I'm starting to forget these things... I'm not writing php anymore :)). I'm integrating your tips. I don't instead agree with the idea of creating different secrets for each user. While it's obvious more secure, the advantage is minimal in my opinion in this case.Hartung

© 2022 - 2024 — McMap. All rights reserved.