3

I am interested in coding a remember me feature on a login page that will record a cookie on the persons computer for how ever long. I know there are dangers in this, so I want to make sure I do this the right way.

I have heard about people creating a link between a database table and the cookie itself for verification; however, not sure how to do this or if it is recommended. I am new to cookies, so please explain the best way possible. I don't have any examples because like I said, I have never coded this before and want to make sure I do it the right way and get input from people who have done it.

I'm not sure if facebook's cookies last forever or not, I just know on my computer I never really have to log back in unless I delete my history. So I'd probably want to do it similar to the way they do it.

Thanks in advance!

kdjernigan
  • 309
  • 2
  • 5
  • 14
  • There's no real magic to remember-me cookies; just imagine creating a new random password when you set it. Store that in a database, and whenever the user sends the cookie, use that to log them in instead of their real username and password. When it expires (every cookie expires. Sometimes if not used for a long time, sometimes after a year, sometimes when the user explicitly logs out), delete it from the database. – Dave Mar 16 '13 at 21:00

2 Answers2

3

One of the best techniques for persistent cookies I have seen to date is Barry Jaspan's:

  1. 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.
  3. 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.
    1. 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.
    2. 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.
    3. If the username and series are not present, the login cookie is ignored.

I recommend you read the whole article, the above is the gist.

Additionally you will want to issue these cookies only over an SSL connection and set the secure and httponly flags on it.

Since this means the user is only authenticated while connecting to the server via SSL, I additionally set the Strict-Transport-Security HTTP header to force the browser to always use SSL in the future and additionally a simple cookie like persistent_login_available=yes for non-SSL connections. If the browser visits the site over a non-SSL connection and the server sees the persistent_login_available cookie, it redirects the visitor to the SSL version and authenticates the user via the secure cookie.

With this process you're about as secure as can be. It may be a little over the top for your needs though, you decide.

deceze
  • 510,633
  • 85
  • 743
  • 889
  • I appreciate the article and it def. gave me some pointers. I more or less wasn't sure how to code this either but someone below gave me an example of how to code it. Can I get your input on the overall code concept and make sure it is a good code to use? – kdjernigan Mar 17 '13 at 02:16
3

I've not coded this for myself yet, but I would approach the problem like this:

1. Create a table which can be used to force a validity check when a user presents a cookie for persistence:

create table RememberMe
(
   user_id    int(10)  NOT NULL,
   user_token char(10)  NOT NULL,
   token_salt int(6)   NOT NULL,
   time       int(10)  NOT NULL,
   PRIMARY KEY (user_id),
   CONSTRAINT nameYourConstraint 
              FOREIGN KEY (user_id) 
              REFERENCES userTableName (whatever_user_id_equals)
)

To populate this table I would add some lines of code to the login, for this example I'll use pseudo-code

// userID variable has been sanitized already so
// check if user clicked remember me
// and if the user logged in successfully:
if ( rememberMe == checked && login() == true )
{
     // random number to serve as our key:
     randomNumber = random( 99, 999999  );

     // convert number to hexadecimal form:
     token = toHex( ( randomNumber**randomNumber ) );

     // encrypt our token using SHA1 and the randomNumber as salt
     key = encrypt( token, randomNumber, SHA1 );

     // get the number of seconds since unix epoch:
     // (this will be 10 digits long until approx 2030)
     timeNow = unix_time()

     // check to see if user is in table already:
     sql = "SELECT user_id FROM RememberMe
            WHERE user_id = 'userID'";

     // connect to database:
     db = new DBCon();
     
     result = db->query( sql );
     
         // number of rows will always be 1 if user is in table:
         if ( result->rows != 1 )
             exists = true;
         else 
             exists = false;
     
     result->free_memory();
     
         if ( exists == true )
         {
             sql = "UPDATE RememberMe SET
                    user_id    = 'userID'
                    user_token = 'token'
                    token_salt = 'randomNumber'
                    time       = 'timeNow'";
         }
         else
         {
             sql = "INSERT INTO RememberMe
                    VALUES( 'userID', 'token', 'randomNumber', 'timeNow' )";
         }
     
     result = db->query( sql );
     
         // the affected rows will always be 1 on success
         if ( result->affected_rows != 1 )
         {
            print( "A problem occurred.\nPlease log in again." );
            quit();
         }

     result->free_memory();

     // create a new cookie named cookiemonster and store the key in it:
     // (we're not actually storing a score or birthday, its a false flag)
     set_cookie( "CookieMonster", escape("score="+ userID +"birthday="+ key );
}

What this code does is check if the user has checked the remember me and it populates the database table with a key, a token, and a salt for the user as well as a time (so that you can enforce time restraints on the remember me feature).

From here you can add code to your website that checks if the CookieMonster cookie is set and if it is you can follow these steps to enforce its validity:

  1. extract the userID and the key from the cookie presented

  2. Query the database with userID to see if

       --> a) user has requested to be remembered
    
       --> b) check the time to see if they cookie is still valid
    
       --> c) extract the token and salt from database table record
    
  3. Run the token and salt through an encrypt() function call and match against the presented key.

  4. If everything checks out, create a new session and log the user in.

Now every time the user comes to your site they will be logged in, and in the event that their computer is compromised the attacker will not have access to their password

Side Note: You should always require your user to present a password when changing their password or email, this way should a user's cookie find its way into the wrong hands your attacker will be unable to steal the account.

Community
  • 1
  • 1
Charles D Pantoga
  • 4,307
  • 1
  • 15
  • 14
  • I like this...I noticed in your login script, all it did was update the token and all the details..it didn't actually check to see if the cookie existed before...I'm a little confused about that. Shouldnt this be done after it checks to see if the token and all that are correct between table and cookie, THEN update the details to new numbers with new time? Or what does that do that I'm not seeing? – kdjernigan Mar 17 '13 at 02:14
  • 1
    Yeah, sorry I know its a little confusing I was just trying to get the idea across. I said somewhere in there (i think!) that some code needs to be added to check if the cookie is set.This is all pseudo-code so that it could be ported into whatever language. Most of the functions were based off of functions in the PHP library, however. – Charles D Pantoga Mar 17 '13 at 03:31
  • 1
    All pages on your website should check for the cookie, not just a single page (any page that could be an entry point, at least). If the cookie is set, take the user ID and grab the info from the DB. If its not, this code gets run when the user logs in. Lets say you want the 'remember me' function to remember people for two weeks [ this is specified in your code by taking the current time in seconds and subtracting from it the time that the cookie was set. Once you've done the subtraction you can compare the result to the time limit.] – Charles D Pantoga Mar 17 '13 at 03:32