Building a CHAP Login System: Coding Server-Side Random Seeds - Refactoring the CHAP login system: Adding functionality to the JavaScript program
(Page 4 of 4 )
Having defined the server-based random string generator, what I’ll do next is to introduce some minor changes to the original CHAP script, so it will be able to work with the brand new PHP functions. Also, the script will be provided with a slightly more elegant validation routine, by using some DOM methods. Thus, here is the improved version of the CHAP script:
/*
* 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;
}
As you can see, I’ve kept the same core structure for the JavaScript program. The logic for transmitting hashed strings is completely maintained, so the main difference rests on the use of the “getChallengeVar()” function to obtain the challenge string sent by the server. The following line of code illustrates what I just said:
chlng.value=MD5(MD5(psw.value)+'<?php echo getChallengeVar()?>');
Regarding the JavaScript code, I’ve also added a couple of functions, “showError()” and “hideError()” respectively, which are useful for implementing a more elegant method to display error messages when the offending fields hasn’t been filled in, instead of triggering ugly alerts. In short, the “showError()” function appends a new paragraph to the document tree, which shows a custom error message according to the field currently validated. Its counterpart, “hideError()” removes the paragraph when the user changes the value of the offending field.
Of course, you can write your own client-side validation mechanism, or even omit it entirely. Since JavaScript-based validation isn’t the primary goal of the series, I’ll skip over the topic, before you get bored with irrelevant details.
With the improved version of the “doCHAP()” function already defined, all I have to do is attach it to an “onsubmit” event handler, as follows:
/*
* 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();
}
}
}
Once the client has transmitted the corresponding hashed value to the server, a simple procedural script for authenticating the client might be defined in the following way:
// 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>';
}
As you can see, above I’ve coded a simple script that authenticates the client by obtaining the user ID value directly from a database table. Then, the same MD5 calculation is performed on the server and compared to the value transmitted by the client. If the hashed string along with the user ID match the values obtained by the server, then the login is considered successful. Otherwise, the client is simply rejected.
Notice that I’ve purposely kept the code as clear as possible, by paying attention only to the authentication process. Of course, you might want to introduce some changes, such as redirecting the user to another page in case the login fails, or whatever mechanism you wish to implement within your web programs. For exemplifying a CHAP login system, the code is indeed very explanatory.
Summary
By this point, you’ve been provided with some clear examples of how a CHAP login system can be developed for straight inclusion in web-based applications. Besides, I’ve illustrated some relevant aspects related to client-side data encryption, enough to get you started on developing a login system, either expanding the one described above or building your own version.
In the last part of this series, I’ll be developing a PHP object-oriented solution on the server side, instead of working with a procedural method, while maintaining the overall structure for JavaScript program that you just saw. See you in the last part!
| 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. |