14

I was wondering what the safest way of a cookie login is? If you just store the pass(encrypted with salt) and username in the cookie and validate it against the user table, a potential attacker can steal a cookie and login. People normally don't check there 'last time online'.

So is there a better way for the 'remember me cookie'? IP isn't a good option, is it? (Some machines change IP all the time).

Paul Dixon
  • 295,876
  • 54
  • 310
  • 348
SuperSpy
  • 1,324
  • 3
  • 13
  • 28
  • 1
    Please tell us something about your application that would use this feature. What would the potential impact of an impersonation/identity theft be? – Gumbo Jan 23 '11 at 11:49
  • most machines change IP all the time... – Shoe Jan 23 '11 at 11:49
  • 2
    "remember me" feature is insecure by definition. So, it's up to you to choose between usability and paranoia. – Your Common Sense Jan 23 '11 at 12:11
  • 1
    Thats not a fact Col. I can limit the options of the user when logged in via a cookie (he has to revalidate when wanting to edit details etc.) and I can limit the window for an attacker. – SuperSpy Jan 23 '11 at 13:30
  • 1
    The "remember me" feature is *not* insecure, technically. It may be insecure because the user shares their device with other people or because they may lose their device. You should re-use an existing authentication framework whenever possible, because, really, it's complex. For example, take a look at https://github.com/delight-im/PHP-Auth – caw Sep 21 '16 at 02:34

3 Answers3

21

I think I've found a clever solution!

Advantages of this (complicated?) script:

  • 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 the user's username, a series identifier, and a token. The series and token are unguessable random numbers from a suitably large space. All three are stored together in a database table.
  • When a non-logged-in user visits the site and presents a login cookie, the username, series, and token are looked up in the database.
  • If the triplet is present, the user is considered authenticated. The used token is removed from the database. A new token is generated, stored in database with the username and the same series identifier, and a new login cookie containing all three is issued to the user.
  • If the username and series are 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.
  • If the username and series are not present, the login cookie is ignored.

I've made a table in the database with the following information:

    session | token | username | expire

The remember me cookie will have this setup:

    $value = "$session|$token|$userhash"; //Total length = 106
  • Session will be a string of 40 (sha1) characters.
  • Token will be a string of 32 (md5) characters.
  • Userhash in the cookie will be a string of 32 (md5 of username) characters.
  • Username in the database will be the normal username.
  • Expire will be now + 60 days.

The script:

if(isset($_SESSION['check']) || $_SESSION['check']){
    //User is logged in
}else if(isset($_COOKIE['remember']) && strlen($_COOKIE['remember'])==106){
    //THERE is a cookie, which is the right length 40session+32token+32user+2'|'
    //Now lets go check it...
    conncectdb(); //Sets connection
    //How do I protect this script form harmful user input?
    $plode = explode('|',$_COOKIE['remember']);
    $session = mysql_real_escape_string($plode[0]);
    $token = mysql_real_escape_string($plode[1]);
    $userhash = mysql_real_escape_string($plode[2]);
    $result = mysql_query(" SELECT user 
                FROM tokens 
                WHERE session = '$session' 
                AND token = '$token'
                AND md5(user) = '$userhash';")
    if(mysql_num_rows($result)==1){
        //COOKIE is completely valid!
        //Make a new cookie with the same session and another token.
        $newusername = mysql_result($result,0,0);
        $newsession = $session;
        $newtoken = md5(uniqid(rand(), true));
        $newuserhash = md5($username);
        $value = "$newsession|$newtoken|$newuserhash";
        $expire = time()+4184000;
        setcookie('remember', $value, $expire, '/', 'www.example.com', isset($_SERVER["HTTPS"]), true);
        mysql_query("   UPDATE tokens 
                SET token='$newtoken', expire='$expire'
                WHERE session = '$session' 
                AND token = '$token'
                AND md5(user)='$userhash';");
        //Set-up the whole session (with user details from database) etc...
    } else if(mysql_num_rows(mysql_query("SELECT user FROM tokens WHERE session = '$session' AND md5(user) = '$userhash';"))==1)){
        //TOKEN is different, session is valid
        //This user is probably under attack
        //Put up a warning, and let the user re-validate (login)
        //Remove the whole session (also the other sessions from this user?)
    } else {
        //Cookie expired in database? Unlikely...
        //Invalid in what way?
    }
} else {
    //No cookie, rest of the script
}

Advantages of the script:

  • Multiple login. You can create new sessions for each computer you're on.
  • Cookie and database will stay clean. Active users renew there cookie every login.
  • The session check at the beginning ensures that the database will not get useless requests.
  • If an attacker steals a cookie, it gets a new token, but not a new session. So when the real user visits the website with the old(invalid) token but WITH a valid user-session combination the user gets a warning of the potential theft. After re-validating by logging in a new session is created and the session the attacker holds is invalid. The re-validating ensures the victim really is the victim, and not the attacker.

Reference: http://jaspan.com/improved_persistent_login_cookie_best_practice

Cartier
  • 429
  • 4
  • 15
SuperSpy
  • 1,324
  • 3
  • 13
  • 28
  • I see no seinse in this solution. What's the purpose of session field in this table? why dedicated table at all? Why not to store just a hash made out of some user's data? – Your Common Sense Jan 23 '11 at 13:13
  • Any other solution will let you have multiple session as well, without additional table, without over-complicating your script – Your Common Sense Jan 23 '11 at 13:17
  • This limits the time of the attacker being able to use your account. – SuperSpy Jan 23 '11 at 13:22
  • Have you even read the script Col.? You're already punishing me =S – SuperSpy Jan 23 '11 at 13:33
  • 1
    Sounds a lot like this technique: http://jaspan.com/improved_persistent_login_cookie_best_practice – Paul Dixon Jan 23 '11 at 13:46
  • It's what I used indeed =) But the thing thay didn't add is that the user needs to re-validate (and the actual code). The flaw in there script is that an attacker which waits until the victim has a new token, can destroy all his session by trying to authenticate with the old token. – SuperSpy Jan 23 '11 at 13:49
  • I like the implicit session hijacking-detection built into this validation scheme. However, the session hijack will in fact only be detected if someone used the remember-me feature. I'd like to have it optional, but I think I'll still use a detection similar to what you proposed here. – Manuel Strausz Jan 27 '11 at 15:56
  • So you'll create a cookie without asking permission? – SuperSpy Jan 27 '11 at 19:05
6

Most popular ways:

  • Many scripts uses some kind of session tracking. When the user first visits the website, it generates a unique random ID for the user and store session information in the server and the ID in the cookie. The server then identifies the user using the unique ID (called session ID). The information associated with the session ID can be seen only by the server. PHP uses this by default,

  • Some stores the user data in the cookie itself, but with a HMAC signature using a secret string as a key. The script discards the cookie if the signature doesn't match. This way, the server does not have to keep the session data on the server. The user sees what's in the session by looking at the cookie, so you should not store sensitive data in it. Just the user ID (and possibly login time and cookie expiry time) should be enough. Though the user can see what's in the session info, the signature in the cookie makes sure that the user cannot modify the session data himself.

These ways provide some security, that the user can't tamper with the session data, but it doesn't protect the user from eavesdropper. They can always use a packet sniffer and steal a session from any open WiFi network. Some apps do rely on the user IP but it doesn't matter if the attacker is in the same network. Some apps rely on User-Agent, but there will be problems when the user updates their browser or import data from another browser.

If you are really concerned about security, then use HTTPS.

Also read this article, especially the section called How do website operators fix the problem?

Thai
  • 10,746
  • 2
  • 45
  • 57
  • Long text and not a single word on topic. And an upvote from one who don't read but just impressed by text size. – Your Common Sense Jan 23 '11 at 12:14
  • 1
    Isn't the topic about the login cookies and its security? That's what I'm talking about in the answer. – Thai Jan 23 '11 at 13:58
  • It's totally in-topic and it gave me some more ideas about Session IDs and HTTPS stuff. I did upvoted. – Shoe Jan 23 '11 at 14:42
6

Such a “remember me” feature is always an additional security risk.

Because just like in a session you only have one identifier that suffices not just to identify a user (Who is it?) but also to authenticate that user (Is it really him/her?) without doing an actual authentication.

But in opposite to a session that has (or should have) just a short life time (mostly less than an hour) and the identifier is (or should be) changed periodically (time based and on necessity due to authenticity/authority state changes), a “remember me” identifier is valid for days if not even for months or years! And this long validity period poses an additional security risk.

So before asking how to implement such a “remember me” feature, you should ask yourself if you really want that additional security risk. That mainly depends on the assets that your application has and what purpose the authentication has and if you want to take the risk of impersonations/identity thefts that the “remember me” feature poses.

If so, make sure to provide basic security by using HTTPS and set the HTTPOnly flag and the secure flag in your cookies. Then you could do the following to build such a “remember me” feature:

  • Authentication request
    If the user authenticated via HTTPS and set the “remember me” option, generate a random remember me token, store it on the server side in a “remember me” database, and set the remember me cookie with the secure flag with that value. Then start a new session and set a remember me flag.

  • Any other request

    1. If there is no current session, redirect to the remember me page via HTTPS that checks whether there is a remember me cookie. If there is a remember me token and it is valid, invalidate it, generate a new one, store it in the “remember me” database, set a cookie with that new token and create a new session with the remember me flag set. Otherwise redirect to the login page.
    2. If the current session is invalid (make sure to use a strict session invalidation), redirect to the remember me page via HTTPS if the remember me flag is set; otherwise redirect to the login page.

With this the authentication is secured via HTTPS, both the initial authentication and the “remember me” authentication. And the user is only authentic during the current session; if it expires, the user has to re-authenticate either by using the remember me token or providing his/her login credentials. And as the remember me tokens are stored in the database, the user can invalidate any existing remember me token.

Community
  • 1
  • 1
Gumbo
  • 643,351
  • 109
  • 780
  • 844
  • 'if you really want that additional security risk' I can limit the cookie-authenticated-user to prevent damage. Check my script below, you'll never get a lucky random token because if the tokens are identical, the user must be identical two. I want to make the remember me more permanent, rather then just fixating the session expire to a higher value. – SuperSpy Jan 23 '11 at 13:44
  • This is an answer, but (for me) not THE answer. – SuperSpy Jan 23 '11 at 14:21