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 - Using a procedural script: a quick overview of the previous login system (Page 2 of 4 )
Before you look at the approach you can take to building a PHP session management class, the first thing that I’ll do is show the procedural script that implements the CHAP login system. Of course, both client and server processing will be covered, which means providing the code for the JavaScript program along with the PHP authentication script.
Let’s begin listing the code for a sample “login.php” file, which implements the CHAP approach:
<?php /* * Begin of server-side processing */ 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; } function setChallengeVar($name='challenge'){ if(!is_string($name)||!$name){ trigger_error('Invalid variable name'); exit(); } session_start(); // register session variable $_SESSION[$name]=getRandomString(); } function getChallengeVar($name='challenge'){ if(!$_SESSION[$name]){ trigger_error('Invalid variable name'); exit(); } return $_SESSION[$name]; } setChallengeVar(); /* * End of server-side processing */ ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html> <head> <title>CHAP LOGIN SYSTEM EXAMPLE</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> <script language="javascript" src="md5.js"> /* * include MD5 JavaScript library */ <script language="javascript"> /* * validate form fields & * implement the Challenge Handshaking Authentication Protocol */ function doCHAP(){ 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 getChallengeVar()?>'); // 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 'doCHAP()' 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 doCHAP(); } } } </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>
By this point, the code included in the above file should be very familiar to you, so for the sake of completeness I’ll only refer to it by dissecting the portions of code that delimit server and client processing respectively. Of course, the package of PHP functions comprising “getRandomString()”, “setChallengeVar()” and “getChallengeVar()” constitutes the core engine for getting a random string, as well as setting and obtaining challenge session variables.
On the client side, the “md5.js” file incorporates the MD5 JavaScript library, which is used to calculate MD5 hashes on user data. By complementing the power of the library, the set of functions “doCHAP()”, “showError()” and “hideError()” applies the CHAP method on the login form by encrypting the password along with the challenge string, so sensitive data is never passed to the server as plain text.
Now, let’s take a look at the client authenticating script, which is included in the “processlogin.php” file:
// connect to MySQL if(!$db=mysql_connect('host','user','password')){ trigger_error('Error connecting to the server '.mysql_error()); exit(); } // select database if(!mysql_select_db('database',$db)){ trigger_error('Error selecting database '.mysql_error()); exit(); } // run query $sql="SELECT userid,password FROM users WHERE userid='".$_POST['userid']."'"; if(!$result=mysql_query($sql,$db)){ trigger_error('Error running query '.$sql.mysql_error()); exit(); } // check if user ID is valid if(mysql_num_rows($result)<1){ trigger_error('User not found into database'); exit(); } // get user data if(!$row=mysql_fetch_assoc($result)){ trigger_error('Error retrieving user data'); exit(); } session_start(); $challenge=$_SESSION['challenge']; // 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><h1>Thank you for logging in!</h1></body></html>'; } else{ echo '<html><head><title>Access denied!</title><body><h1>Access denied!</h1></body></html>'; }
First, the above script connects to MySQL and runs a query to get the password and user ID that matches the ID passed through the login form. Then, once these values are obtained, the script calculates the same MD5 checksum of the hashed password and the challenge string combined, for comparison with the values transmitted by the client. If a match is found, the client has logged in successfully. Otherwise, the login process simply fails.
Now that you’ve seen an example of how a procedural method can be used to implement CHAP login systems, the next step will consist of modifying the pertinent files to work with a PHP session handling class. Therefore, keep on reading to find out how this is done.