picture of Tim Perdue
When I started seeing spam messages posted to the new column annotation system, I knew I would have to create some sort of user authentication system that helps weed out the losers. I'm the type that would rather write an entire library myself than try to learn something like PHPLib or other similar libraries.
The library needed to handle registration, confirmation emails, account updates (passwords, emails) among other things. It also needed to be secure while not creating a burden on my overloaded database.
So the new system needed to rely on cookies while not being totally exploitable. It was an interesting dilemma. I knew I couldn't simply set a user_name cookie when they logged in (the user name cookie is easy to spoof). I also knew I didn't want to set a simple hash and have to confirm that hash against my database.
The solution was to set both. A user_name cookie is set, along with a hash. The hash is an md5() hash of the user_name as well as a super-secret variable that only PHPBuilder knows. Since md5() is a one-way hash and is, for all intents and purposes, going to secure practically any website, but should not be taken to be "uncrackable"*. I could safely create a hash of the email, which is a known variable, plus the secret variable. It's kind of a public-key/private-key kind of system.
The interesting thing about this system is that it could scale up almost infinitely. Since the hard work of this system is done by md5() on the web server, additional servers can be dropped in incrementally to handle the load. The same is not true of an auth system that hammers a database - the database itself eventually becomes the bottleneck.
* This is a correction requested by the author. Please see comments below for clarification.
Here are the two critical functions in this library - the token creation and token verification functions. Don't worry - the rest of the library is included here as well.
 
<?php

$hidden_hash_var
='your_secret_password_here';

$LOGGED_IN=false;
unset(
$LOGGED_IN);

function 
user_isloggedin() {
    global 
$user_name,$id_hash,$hidden_hash_var,$LOGGED_IN;
    
//have we already run the hash checks?
    //If so, return the pre-set, trusted var
    
if ( isset($LOGGED_IN) ) {
        return 
$LOGGED_IN;
    }
    
//are both cookies present?
    
if ($user_name && $id_hash) {
        
/*
            Create a hash of the user name that was 
            passed in from the cookie as well as the 
            trusted hidden variable

            If this hash matches the cookie hash,
            then all cookie vars must be correct and
            thus trustable
        */
        
$hash=md5($user_name.$hidden_hash_var);
        if (
$hash == $id_hash) {
            
//hashes match - set a global var so we can 
            //call this function repeatedly without 
            //redoing the md5()'s
            
$LOGGED_IN=true;
            return 
true;
        } else {
            
//hash didn't match - must be a hack attempt?
            
$LOGGED_IN=false;
            return 
false;
        }
    } else {
        
$LOGGED_IN=false;
        return 
false;
    }
}

function 
user_set_tokens($user_name_in) {
    
/*
        call this once you have confirmed user name and password
        are correct in the database
    */
    
global $hidden_hash_var,$user_name,$id_hash;
    if (!
$user_name_in) {
        
$feedback .=  ' ERROR - User Name Missing When Setting Tokens ';
        return 
false;
    }
    
$user_name=strtolower($user_name_in);

    
//create a hash of the two variables we know
    
$id_hashmd5($user_name.$hidden_hash_var);

    
//set cookies for one month - set to any amount 
    //or use 0 for a session cookie

    
setcookie('user_name',$user_name,(time()+2592000),'/','',0);
    
setcookie('id_hash',$id_hash,(time()+2592000),'/','',0);
}

?>