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_hash= md5($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);
}
?>