Building a CHAP Login System: An Object-Oriented Approach
Welcome to the last part of the series “Building a CHAP login system.” In three parts, this tutorial provides the basics for building secure login forms, by utilizing the Challenge Handshake Authentication Protocol (known popularly as CHAP). The programming foundation of the system resides primarily on the implementation of the cryptographic MD5 hashing algorithm in JavaScript for sending out encrypted data to the server, which in turn authenticates the client.
Building a CHAP Login System: An Object-Oriented Approach - Putting the pieces together: integrating the “ChallengeGenerator” class (Page 4 of 4 )
Definitely, coupling the PHP class to the existing CHAP script is pretty straightforward, since I’ll only replace the pieces of procedural code with the class itself. Considering that the previous login system is quite familiar to you, below is the complete code that implements the system, this time utilizing the “ChallengeGenerator” class that you just saw:
<?php /* * Begin of server-side processing */ class ChallengeGenerator{ // constructor function ChallengeGenerator($clearSession=true){ if($clearSession){ $this->clearVars(); } session_start(); } // public method clearVars() function clearVars(){ // destroy existing session session_start(); session_unset(); session_destroy(); } // public method setChallengeVar() function setChallengeVar($name='challenge'){ if(!is_string($name)||!$name){ trigger_error('Invalid variable name'); exit(); } // register session variable $_SESSION[$name]=$this->getRandomString(); } // public method getSessionVar() function getChallengeVar($name){ if(!$_SESSION[$name]){ trigger_error('Invalid variable name'); exit(); } return $_SESSION[$name]; } function deleteChallengeVar($name){ if(!$_SESSION[$name]){ trigger_error('Invalid variable name'); exit(); } unset($_SESSION[$name]); } // private method "getRandomString()" function getRandomString($length=40){ if(!is_int($length)||$length<1){ trigger_error('Invalid length for random string'); exit(); } $chars= "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; $randstring=''; $maxvalue=strlen($chars)-1; for($i=0;$i<$length;$i++){ $randstring.=substr($chars,rand(0,$maxvalue),1); } return $randstring; } } // instantiate a ChallengeGenerator object $chlgen=&new ChallengeGenerator(); // register challenge variable $chlgen->setChallengeVar(); /* * End of server-side processing */ ?> <!doctype html public "-//W3C//DTD HTML 4.01//EN"> <html> <head> <title>CHAP LOGIN SYSTEM EXAMPLE</title> <script language="javascript" src="md5.js"></script> <script language="javascript"> /* * validate form fields & * implement the Challenge Handshaking Authentication Protocol */ function checkForm(){ valid=true; // get 'userid' field var usrid=document.getElementById('userid'); if(!usrid){return}; if(!usrid.value){showError(usrid,'Enter your ID')}; // get 'password' field var psw=document.getElementById('passwd'); if(!psw){return}; if(!psw.value){showError(psw,'Enter your password')}; // get 'challenge' field var chlng=document.getElementById('challenge'); if(!chlng){return}; // make MD5 hash of password and concatenate challenge value // next calculate MD5 hash of combined values chlng.value=MD5(MD5(psw.value)+'<?php echo $chlgen->getChallengeVar 'challenge')?>'); // clear password field psw.value=''; return valid; } /* * display error messages */ function showError(obj,message){ if(!obj.errorNode){ obj.onchange=hideError; var p=document.createElement('p'); p.appendChild(document.createTextNode(message)); obj.parentNode.appendChild(p); obj.errorNode=p; } valid=false; return } /* * hide error messages */ function hideError(){ this.parentNode.removeChild(this.errorNode); this.errorNode=null; this.onchange=null; } /* * execute 'checkForm()' function when page is loaded */ window.onload=function(){ var W3CDOM=document.getElementById&&document. getElementsByTagName&&document.createElement; // check if browser is W3CDOM compatible if(W3CDOM){ document.getElementsByTagName('form') [0].onsubmit=function(){ return checkForm(); } } } </script> </head> <body> <!-- login form --> <form method="post" action="processlogin.php"> User ID <input type="text" name="userid" id="userid"/><br /> Password <input type="password" name="passwd" id="passwd"/><br /> <input type="hidden" name="challenge" id="challenge" /> <input type="submit" name="login" value="Log In" /> </form> </body> </html>
With reference to the above snippet, you can appreciate that the programming logic is basically the same. I’ve only included the corresponding class for obtaining server-provided random seeds, but the rest of the script remains nearly identical. The only different piece of PHP code embedded into the markup involves calling to the “getChallengeVar()” method, illustrated in the following line:
To complete the example, the PHP script that runs on the server for authenticating the client might be written in the following way:
// include ‘challenge generator class’ require_once('challengeclass.php'); // include ‘MySQL abstraction class’ require_once('mysqlclass.php'); // instantiate a ChallengeGenerator object $chlgen=&new ChallengeGenerator(false); // get challenge variable $challenge=$chlgen->getChallengeVar('challenge'); // get a "fresh" version of the page header('Cache-control:must-revalidate'); // connect to MySQL $db=new MySQLarray('host'=>'host','user'=>'user','password'=>'password', 'database'=>'database')); // run query to obtain user data $result=$db->query("SELECT userid,password FROM users WHERE userid='".$_POST['userid']."'"); $row=$result->fetchRow(); // check to see if user credentials are valid if(md5($row['password'].$challenge)==$_POST['challenge']&&$row ['userid']==$_POST['userid']){ echo '<html><head><title>Login Successful</title><body><h1>Thank you for logging in! </h1></body></html>'; } else{ echo '<html><head><title>Access denied!</title><h1>Access denied!</h1></body></html>'; } // delete session data and destroy session $chlgen->clearVars();
Using an object-oriented approach, the authenticating script is much simpler to read and understand. First, I’ve included the “ChallengeGenerator” class along with a “MySQL” wrapping class, useful for performing database-related tasks. Then, a new “ChallengeGenerator” object has been instantiated, in order to get the “challenge” session variable previously registered on the login script. These steps are performed with the code listed below:
// instantiate a ChallengeGenerator object $chlgen=&new ChallengeGenerator(false); // get challenge variable $challenge=$chlgen->getChallengeVar('challenge');
Next, the script connects to MySQL, fetches the password and user ID that matches the ID entered by the user, and finally performs the client’s authentication by comparing the MD5 hashed values, in a manner similar to the process implemented in the procedural script. The following code is responsible for authenticating the client:
if(md5($row['password'].$challenge)==$_POST['challenge']&&$row ['userid']==$_POST['userid']){ echo '<html><head><title>Login Successful</title><body><h1>Thank you for logging in! </h1></body></html>'; } else{ echo '<html><head><title>Access denied!</title><h1>Access denied!</h1></body></html>'; }
At the end of the script, all of the session data is deleted and the session itself is destroyed:
// delete session data and destroy session $chlgen->clearVars();
Now, you have an object-oriented script that uses a CHAP system for logging into a web-based application. Of course, it’s just a matter of taste and particular needs what approach you’ll use within your programs. In either case, the system’s effectiveness is the same, since in all cases passwords are never transmitted as plain text. Even if you’re not well versed in cryptographic techniques, you can still use their benefits for improving the overall security of your website’s login forms.
Conclusion
Over this series, I’ve explained the advantages of implementing CHAP systems on login forms, as an efficient and easy-to-use technique to raise the security level for transmitting sensitive data, and particularly passwords, during the execution of web-based programs.
Ranging from a procedural approach to an object-oriented method, you’ve been provided with enough code to play with, and start using the examples for your own convenience and purpose. Of course, I must thank Paul Johnston, who gently allowed me to use his excellent JavaScript MD5 library, throughout the examples developed in this series. Happy encrypting!
DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ real-world tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware.