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:
extract the userID and the key from the cookie presented
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
Run the token and salt through an encrypt() function call and match against
the presented key.
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.