Security
|
|
|
|
<?php //FOREWORD///////////////////////////////////////////////////////////////////////////////////////////
//***Please read considerations***
//***This script was written by Russell Hanby, a php-scriptkiddie.
//***Russell says: "Use it and modify it at your discretion. I want to learn, so help me out.
//*** Comments? Critiques? Email me: russellhanby@yahoo.com. Thanx and enjoy."
// HOW IT WORKS:
// The goal of login.inc was to create an authentication scheme that was as secure as my novice
// abilities could make it, and also easy to use on any page. The core of the scheme is the token
// object which stores several authentication variables. These variable include a username,
// password, timestamp, remote addr, remote hostname, and session_id. Alone, these variable are
// seemingly arbitrary because any one of them could be potentially forged. However, when combined
// and compared against multiple sources, these variables create a strong scheme with which to
// authenticate users.
//CONTENTS:
// FUNCTIONS: login_check(), ssl_redirect(), login(), enc_token(), dec_token(), valid_token(),
// create_token(), get_key(), submitting(), form_input_check(), loggin_in(), logged_in(),
// process_user_pass()
// CLASSES: token
//
//WHAT YOU NEED:
// FILES: user_pass file, key file
// MODULES: mcrypt support
//
//CONSIDERATIONS:
// Encryption - the encryption used for the token is RIJNDAEL_256. The key is derived "randomly"
// each time the enc_token() function is called by first picking a random "key coordinate" and then
// passing this $kc to the get_key() function. This function opens the key_file and extracts a 32
// character key starting at the $kc point in the file. The beauty of this is that the token
// never appears the same, the token is cookie safe, and the key_file can be changed at anytime.
// Key File- a file containing 10,000 "random" ASCII characters in the 32 to 255 range. This
// file can be created and recreated with the following code:
// function create_key_file()
// {
// $path = 'c:pathtokey_file.ext';
// if(!fopen($path, "w")) echo("file write failed");
// else
// {
// $counter = 1;
// while($counter < 10000)
// {
// $chr = chr(mt_rand(33,255));
// fwrite($file,$chr);
// $counter += 1;
// }
// fclose($file);
// }
// }
// SSL - checking for ssl and appropriately redirecting is supported. Simply uncomment the lines in
// the login_check() function and the ssl_redirect() function.
// SESSION - session_start() is called at the beggining of the file, so consider this when writing
// session code outside of the file.
// PROCESS_USER_PASS - this function does not account for the use of salt with the stored usernames
// and passwords. This functionality can be added by adding a $salt variable to the function.
// LOGIN - the login_check() function is set up to display a default login form if the user is not
// authenticated or if the user has not logged in. You can edit this form and the way it is
// displayed by editing both functions. Also, it may be helpful to move the login() function call
// outside of the login_check() function, and include it in your control on your page.
//
// HOW TO USE LOGIN.INC:
// (1) create user_pass file - storing usernames and passwords in the following format:
// "user:passnr", where user and pass are the md5 hashs (without salt) of the valid users, and
// "nr" is the new line delimiter.
// (2) create key_file - use the function above to create such a file. If you choose to use your
// own key_file, make sure to edit the get_key() function to account for a different size of file.
// Also, you can recreate a new key_file (for added security) at any time using this function.
// (3) login.inc - include the file at the beggining of any page you wish to require
// authentication.
// (4) login_check - add control to page after include. For example:
// if(login_check()) { insert code if user is authenticated; }
// else { insert code if user is not authenticated; }
// (5) you're done!
/////////////////////////////////////////////////////////////////////////////////////////////////////
session_start();
/////////////////////////////////////////////////////////////////////////////////////////////////////
// Function Name : login_check;
// Function Purpose: determines status of user and //calls appropriate functions;
// Recieves : nothing;
// Returns : bool;
///////////////////////////////////////////////////////////////////////////////////////////////////// function login_check()
{
if(logged_in())
{
if(valid_token())
{
return true;
}
else return false;
}
elseif(logging_in())
{
if(process_user_pass(md5($_POST['USER']),md5($_POST['PASS'])))
{
create_token();
unset($_POST['USER']);
unset($_POST['PASS']);
return true;
}
else
{
//If using ssl for login, uncomment the following if statement and
//edit ssl_redirect function
/*if($_SERVER['HTTPS']=="off") ssl_redirect();
else */login($_SERVER['PHP_SELF']);
return false;
}
}
else
{
//If using ssl for login, uncomment the following if statement and
//edit ssl_redirect function
/*if($_SERVER['HTTPS']=="off") ssl_redirect();
else */login($_SERVER['PHP_SELF']);
return false;
}
} /////////////////////////////////////////////////////////////////////////////////////////////////////
// Function Name : ssl_redirect;
// Function Purpose: redirects user to a secure //connection;
// Recieves : nothing;
// Returns : nothing;
///////////////////////////////////////////////////////////////////////////////////////////////////// function ssl_redirect()
{
/*$url1 = "https://";
$url2 = $_SERVER['SERVER_NAME'];
$url3 = $_SERVER['PHP_SELF'];
$url = "$url1$url2$url3";
//either display link for redirection
//echo("You must login over a secure connection...<BR>");
//echo("<A HREF=$url>Click here for a secure connection</A>");
//or use header function to redirect
//header("location: $url");
//or insert your own code here*/ } /////////////////////////////////////////////////////////////////////////////////////////////////////
// Function Name : login;
// Function Purpose: displays login form with provided //action, which is just PHP_SELF, unless
// otherwise specified;
// Recieves : $act - action for processing login //form;
// Returns : nothing;
///////////////////////////////////////////////////////////////////////////////////////////////////// function login($act)
{ $loginform = "<STYLE TYPE=text/css REL=stylesheet>
.loginbox { display: block; position: relative; height: 110px; width: 300px;
font-family: arial; color: blue; border: solid 1px blue; text-align: center;
padding: 8px;}
.header { text-align: center; font-size: 16; font-weight: bold; color: blue;
background-color: #FFD700; padding: 2px; border: solid 1px blue;}
.button { width: 60px; border: solid 1px blue; color: blue; background: white;
cursor: pointer; border: inset 1px blue; padding: 2px;}
</STYLE>
<FORM CLASS=loginbox METHOD=post ACTION=$act>
<TABLE ALIGN=center>
<TR><TD ALIGN=center COLSPAN=2 CLASS=header>Log in...</TD></TR>
<TR><TD>User:</TD><TD><INPUT TYPE=text SIZE=25 MAXLENGTH=20 NAME=USER></TD></TR>
<TR><TD>Pass:</TD><TD><INPUT TYPE=password SIZE=25 MAXLENGTH=25 NAME=PASS></TD></TR>
<TR><TD ALIGN=center COLSPAN=2><INPUT TYPE=submit NAME=LOGIN VALUE=LOGIN CLASS=button></TD></TR>
</TABLE>
</FORM>";
echo($loginform);
} /////////////////////////////////////////////////////////////////////////////////////////////////////
// Class Name : token;
// Class Purpose: stores variables for validation;
// Recieves : $u - plaintext username;
// $p - plaintext password;
// $s - session id;
// $t - time stamp;
// $h - hostname of remote //user;
// $a - ip of remote user;
// Returns : nothing;
// Consideration: username and password are stored //in plaintext so that they can be used for other
// functions if necessary;
///////////////////////////////////////////////////////////////////////////////////////////////////// class token {
private $user;
private $pass;
private $sess_id;
private $time_stamp;
private $host;
private $addr;
function __construct($u,$p,$s,$t,$h,$a)
{
$this->user = $u;
$this->pass = $p;
$this->sess_id = $s;
$this->time_stamp = $t;
$this->host = $h;
$this->addr = $a;
}
function user() { return $this->user; }
function pass() { return $this->pass; }
function time_stamp() { return $this->time_stamp; }
function sess_id() { return $this->sess_id; }
function host() { return $this->host; }
function addr() { return $this->addr; }
} /////////////////////////////////////////////////////////////////////////////////////////////////////
// Function Name : enc_token;
// Function Purpose: serializes, encrypts, and then //base64 encodes bare token object for storage;
// Recieves : $o - token object;
// Returns : $enc_token - encoded/encrypted //token string;
///////////////////////////////////////////////////////////////////////////////////////////////////// function enc_token($o)
{
$ser_token = serialize($o);
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$kc = mt_rand(0, 99968);
$_SESSION['KC'] = $kc;
$key = get_key($kc);
$crypt_token = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $ser_token, MCRYPT_MODE_CBC, $iv);
$enc_token = base64_encode($crypt_token);
return $enc_token;
} /////////////////////////////////////////////////////////////////////////////////////////////////////
// Function Name : dec_token;
// Function Purpose: base64 decodes, decrypts, and //then unserializes token string;
// Recieves : $str - encoded/encrypted string;
// Returns : $unser_token - token object;
///////////////////////////////////////////////////////////////////////////////////////////////////// function dec_token($str)
{
$dec_token = base64_decode($str);
$iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_CBC);
$iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
$kc = $_SESSION['KC'];
$key = get_key($kc);
$decrypt_token = mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $dec_token, MCRYPT_MODE_CBC, $iv);
$unser_token = unserialize($decrypt_token);
return $unser_token;
} /////////////////////////////////////////////////////////////////////////////////////////////////////
// Function Name : valid_token;
// Function Purpose: validates token against SESSION //and SERVER variables,as well as user_pass file;
// Recieves : nothing;
// Returns : bool;
///////////////////////////////////////////////////////////////////////////////////////////////////// function valid_token()
{
$enc_token = $_SESSION['TOKEN'];
$dec_token = dec_token($enc_token);
$sess_id = session_id();
$timestamp = $_SESSION['TIME_STAMP'];
$host = $_SERVER['REMOTE_HOST'];
$addr = $_SERVER['REMOTE_ADDR'];
$token_timestamp = $dec_token->time_stamp();
$token_sess_id = $dec_token->sess_id();
$token_user = md5($dec_token->user());
$token_pass = md5($dec_token->pass());
$token_host = $dec_token->host();
$token_addr = $dec_token->addr();
if($timestamp == $token_timestamp and $sess_id == $token_sess_id and
$host == $token_host and $addr == $token_addr and
process_user_pass($token_user,$token_pass)==true) return true;
else return false;
} /////////////////////////////////////////////////////////////////////////////////////////////////////
// Function Name : create_token;
// Function Purpose: creates token object, calls //enc_token, and stores token in SESSION;
// Recieves : nothing;
// Returns : bool;
///////////////////////////////////////////////////////////////////////////////////////////////////// function create_token()
{
$date = getdate();
$_SESSION['TIME_STAMP'] = $date;
$sess_id = session_id();
$user = $_POST['USER'];
$pass = $_POST['PASS'];
$host = $_SERVER['REMOTE_HOST'];
$addr = $_SERVER['REMOTE_ADDR'];
$token = new token($user,$pass,$sess_id,$date,$host,$addr);
$enc_token = enc_token($token);
$_SESSION['TOKEN'] = $enc_token;
} /////////////////////////////////////////////////////////////////////////////////////////////////////
// Function Name : get_key;
// Function Purpose: gets key from key_file using "key //coordinate"
// Recieves : $kc - "key coordinate";
// Returns : $key - key from file(upon success);
// false(upon failure);
///////////////////////////////////////////////////////////////////////////////////////////////////// function get_key($kc)
{
//CONSIDERATION: there is no error checking for a failed read on the key_file, the function
//will simply return false upon failure, so make sure your key_file is being read properly, or
//add control statement in enc/dec_token functions.
$path = 'C:pathtokey_file.ext';
if(!$file = fopen($path,'r'))
return false;
else
{
$start = $kc;
fseek($file,$start);
$key = fgets($file,33);
fclose($file);
return $key;
}
} /////////////////////////////////////////////////////////////////////////////////////////////////////
// Function Name : submitting;
// Function Purpose: determines if a user is submitting //a form;
// Recieves : nothing;
// Returns : bool;
///////////////////////////////////////////////////////////////////////////////////////////////////// function submitting()
{
if(!isSet($_POST['USER']) or !isSet($_POST['PASS']) or !isSet($_POST['LOGIN'])) return false;
else return true;
} /////////////////////////////////////////////////////////////////////////////////////////////////////
// Function Name : form_input_check;
// Function Purpose: ensures form input is semi-valid;
// Recieves : nothing;
// Returns : bool;
///////////////////////////////////////////////////////////////////////////////////////////////////// function form_input_check()
{
//If you wish, you may insert your own code to scrutinize form_input more thoroughly
if($_POST['USER'] == "" or strlen($_POST['USER']) > 20) return false;
elseif($_POST['PASS'] == "" or strlen($_POST['PASS']) > 25) return false;
else return true;
} /////////////////////////////////////////////////////////////////////////////////////////////////////
// Function Name : loggin_in;
// Function Purpose: runs functions to determine if a //user is logging in;
// Recieves : nothing;
// Returns : bool;
///////////////////////////////////////////////////////////////////////////////////////////////////// function logging_in()
{
if(!submitting()) return false;
elseif (!form_input_check()) return false;
else return true;
} /////////////////////////////////////////////////////////////////////////////////////////////////////
// Function Name : logged_in;
// Function Purpose: determines if a user is logged in;
// Recieves : nothing;
// Returns : bool;
///////////////////////////////////////////////////////////////////////////////////////////////////// function logged_in()
{
if(!isset($_SESSION['Token']) or !isset($_SESSION['TIME_STAMP']) or
!isset($_SESSION['KC'])) return false;
else return true;
} /////////////////////////////////////////////////////////////////////////////////////////////////////
// Function Name : process_user_pass;
// Function Purpose: validates username and password //against user_pass_file
// Recieves : $u - md5 hash of username;
// $p - md5 hash of password;
// Returns : bool;
///////////////////////////////////////////////////////////////////////////////////////////////////// function process_user_pass($u,$p)
{
//CONSIDERATION: if users are not able to be //validated, make sure user_pass_file is being
//read properly. Also, this function assumes //usernames and passwords are stored in the following
//manner- '$u:$pnr'
$path = 'C:pathtouser_pass_file.ext';
if(!$file = fopen($path,"r"))
{
return(false);
}
while(!feof($file))
{
$line = fgets($file, 67);
$user_pass = explode(":",$line);
list($parseduser, $parsedpass) = $user_pass;
$parsedpass = trim($parsedpass);
if($parseduser==$u && $parsedpass==$p)
{
fclose($file);
return(true);
}
}
fclose($file);
return(false);
} /////////////////////////////////////////////////////////////////////////////////////////////////////
?>
|
|
|
Usage Example
|
|
|
Rate This Script
|
|
|
|