Section 7.4. Persistent Logins


7.4. Persistent Logins

A persistent login is a mechanism that persists authentication between browser sessions. In other words, a user who logs in today is still logged in tomorrow, even if the user's session expires between visits.

A persistent login diminishes the security of your authentication mechanism, but it increases usability. Instead of troubling the user to provide authentication credentials upon each visit, you can provide the user with the option of being remembered.

Figure 7-2. An attacker can replay a user's cookie to gain unauthorized access


The most common flawed implementation of a persistent login that I have observed is to store the username and password in a cookie. The temptation is understandablerather than prompting the user for a username and password, you can simply read them from a cookie. Everything else about the authentication process is consistent, so this makes the implementation easy.

If you store the username and password in a cookie, immediately disable this feature and read the rest of this section for some ideas for a more secure implementation. You should also require users who present such cookies in the future to change their passwords because they have been exposed.


A persistent login requires a persistent login cookie, often called an authentication cookie , because a cookie is the only standard mechanism that can be used to persist data across multiple sessions. If this cookie provides permanent access, it poses a serious risk to the security of your application, so you want to be sure that the information you store in the cookie has a restricted window of time for which it can be used to authenticate.

The first step is to devise a method that mitigates the risk posed by a captured persistent login cookie. Although capture is clearly something that you want to avoid, a Defense in Depth approach is best, particularly because this mechanism diminishes the security of an authentication form even when implemented correctly. Thus, the cookie should not be based upon any information that provides permanent access, such as the user's password.

To avoid the use of the user's password, create a token that is valid for a single authentication:

     <?php     $token = md5(uniqid(rand(), TRUE));     ?> 

You can store this token in a user's session in order to associate it with that particular user, but this doesn't help you persist logins across sessions, which is the whole point. Therefore, you must use a different method to associate a token with a particular user.

Because a username is less sensitive than a password, you can store it in the cookie, and this can be used during authentication to determine which user's token is being presented. However, a slightly better approach is to use a secondary identifier that is less likely to be predicted or discovered. Consider a table for storing usernames and passwords that has three additional columns for a secondary identifier (identifier), a persistent login token (token), and a persistent login timeout (timeout):

     mysql> DESCRIBE users;     +------------+------------------+------+-----+---------+-------+     | Field      | Type             | Null | Key | Default | Extra |     +------------+------------------+------+-----+---------+-------+     | username   | varchar(25)      |      | PRI |         |       |     | password   | varchar(32)      | YES  |     | NULL    |       |     | identifier | varchar(32)      | YES  | MUL | NULL    |       |     | token      | varchar(32)      | YES  |     | NULL    |       |     | timeout    | int(10) unsigned | YES  |     | NULL    |       |     +------------+------------------+------+-----+---------+-------+ 

By generating and storing a secondary identifier along with the token, you can create a cookie that does not disclose any of the user's authentication credentials:

     <?php     $salt = 'SHIFLETT';     $identifier = md5($salt . md5($username . $salt));     $token = md5(uniqid(rand(), TRUE));     $timeout = time() + 60 * 60 * 24 * 7;     setcookie('auth', "$identifier:$token", $timeout);     ?> 

When a user presents a persistent login cookie, you can check to see that several criteria are met:

     <?php     /* mysql_connect() */     /* mysql_select_db() */     $clean = array();     $mysql = array();     $now = time();     $salt = 'SHIFLETT';     list($identifier, $token) = explode(':', $_COOKIE['auth']);     if (ctype_alnum($identifier) && ctype_alnum($token))     {       $clean['identifier'] = $identifier;       $clean['token'] = $token;     }     else     {       /* ... */     }     $mysql['identifier'] = mysql_real_escape_string($clean['identifier']);     $sql = "SELECT username, token, timeout             FROM   users             WHERE  identifier = '{$mysql['identifier']}'";     if ($result = mysql_query($sql))     {       if (mysql_num_rows($result))       {         $record = mysql_fetch_assoc($result);         if ($clean['token'] != $record['token'])         {           /* Failed Login (wrong token) */         }         elseif ($now > $record['timeout'])         {           /* Failed Login (timeout) */         }         elseif ($clean['identifier'] !=                 md5($salt . md5($record['username'] . $salt)))         {           /* Failed Login (invalid identifier) */         }         else         {           /* Successful Login */         }       }       else       {         /* Failed Login (invalid identifier) */       }     }     else     {       /* Error */     }     ?> 

You should adhere to three important implementation details to restrict the use of a persistent login cookie:

  • The cookie itself expires in one week (or less).

  • The cookie is good for only a single authentication (delete or regenerate the token after a successful login).

  • A timeout of one week (or less) is enforced on the server.

If you want a user to be remembered indefinitely as long as the user visits your application more frequently than the timeout, simply regenerate the token after each authentication and set a new cookie.


Another useful guideline is to require that the user provide a password prior to performing a sensitive transaction. The persistent login should grant access to only the features of your application that are not considered to be extremely sensitive. There is simply no substitute for requiring a user to manually authenticate prior to performing some sensitive transaction.

Lastly, you want to make sure that a user who logs out is really logged out, and this includes deleting the persistent login cookie:

     <?php     setcookie('auth', 'DELETED!', time());     ?> 

This overwrites the cookie with a useless value and also sets it to expire immediately. Thus, a user whose clock somehow causes this cookie to persist should still be effectively logged out.




Essential PHP Security
Essential PHP Security
ISBN: 059600656X
EAN: 2147483647
Year: 2005
Pages: 110

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net