Session Management Using PHP, Part 2: Server-side Sessions

8
12205

Managing a session from the server side provides a safe mechanism to maintain the session. The session created using this technique can be spread across several pages loaded through many browsers (Firefox, Konqueror, Opera, or even IE). It means that the session does not expire even if one browser crashes and the same URL is loaded through another browser. Security settings of the client machine do not affect server-side session management.

For all these goodies, we have to spend time on a few extra lines of code and provide one extra database table. A table called session_log is created in the session database, with columns for storing a session ID, user ID, IP address of the remote machine, status of the session (enumerated as VALID or EXPIRED, numeric values 1 or 2), start time and last-access time. Entries in the session_log table are made by the login script and updated by the status script.

The script for the display of the user name and password fields for login is given below (the script is saved in a file named dbloginform.html):

<html><head><title>Login</title></head>
<body>
<form method="post" action="dblogin.php">
<center>
<table border=0>
<tr>
<td><label for="username">Username:</label></td>
<td><input type="text" id="username" name="username" maxlength="50" /></td>
</tr>
<tr>
<td><label for="passwd">Password:</label></td>
<td><input type="password" id="passwd" name="passwd" /></td>
</tr>
</table><br />
<input type="submit" value="Log in" />
</form>
</body></html>

The resulting login screen is shown in Figure 1.

Figure 1: Login prompt shown by calling dbloginform.html
Figure 1: Login prompt shown by calling dbloginform.html

Logging in

The login process is handled by the script dblogin.php, which takes the user name and password and checks for the validity of the pair. After doing so, a session ID is generated, which needs to be a unique number. In the present case, the microtime() function with ‘true’ as an argument was called and the result was printed into a string called $t. A while loop was run until the session ID got inserted into the database. The login screen is shown in Figure 2 and the login script is provided below:

<?php
/*dblogin.php*/
$user;
$session_id;
function dblogin($u, $p)
{
global $user, $session_id;
$con = mysql_connect('localhost','user','pass') or die(mysql_error());
mysql_select_db('session', $con) or die(mysql_error());

$res=mysql_query("select id, decode(pass,'session') as pass from user where name='". addslashes($u). "';",$con) or die(mysql_error());
if(mysql_num_rows($res) != 1) {
printf("<center><h1>User %s not found</h1><br /><a href="dbloginform.html">Go to login page</a></center>", $username);
return false;
}
if(mysql_result($res,0,"pass") != $p) {
printf("<center><h1>Login attempt rejected for %s!</h1><br /><a href="dbloginform.html">Go to login page</a></center>", $username);
return false;
}

$uid = mysql_result($res,0,"id");
$t = sprintf("%.12f",microtime(true));
$done = false;
while(!$done) {
$done = true;
mysql_query("insert into session_log values ($t,$uid, ‘$_SERVER[REMOTE_ADDR]',1, now(), now());",$con) or $done=false;
if(!$done)
$t = sprintf("%.12f",microtime(true));
}
$user=$u;
$session_id=$t;
mysql_close($con);
return true;
}

if(dblogin($_POST[‘username'], $_POST[‘passwd']))
printf("<center><b>Welcome %s! Your session id is %s</b><br/>n",$user, $session_id);
print("<a href="dbloginstatus.php">Check session status!</a><br /><a href="dbprotectedimage.php">View Image</a><br /><a href="dblogout.php">Logout</a></center>");
?>

Figure 2: Login screen for server-side session management
Figure 2: Login screen for server-side session management

The login script obtains the IP address of the remote client using the global variable $_SERVER[REMOTE_ADDR]. The user ID is obtained from the user table. The starting and last access times are also inserted into the table. The script begins working from the if condition found at the foot, calls dblogin() function and waits for the boolean result to arrive from the function. The function returns true only if all the checking operations are successfully completed.

Checking session status

Clicking the ‘Check session status!’ link takes us to the status checking script, named dbloginstatus.php. This script checks whether the IP address of the machine requesting the page holds a valid session. The session might expire either by calling dblogout.php (clicking the link or directly entering the URL) or by time out. The time limit is specified by the $time_out variable, which stores the maximum permissible time since the last access. In the present case, the time limit was set to 120 seconds, to quickly test the time-out message. But, in the case of real-world projects, it may vary from 1,200 seconds (20 minutes) to 2,400 seconds (40 minutes).

Status messages for valid sessions and timed-out sessions are shown in Figures 3 and 4. The status monitoring script, given below, should be included at the top of all the pages that require to be protected for the access of valid users:

<?php
/*dbloginstatus.php*/
$user;
$session_id;
$time_out=120;
function checkStatus() {
global $time_out;
$uid;
global $user, $session_id;
$valid=false;
$remote_ip = $_SERVER[‘REMOTE_ADDR'];
$con = mysql_connect('localhost','user','pass') or die(mysql_error());
mysql_select_db("session", $con) or die(mysql_error());
$res = mysql_query("select user_id, session_id, status, time_to_sec(timediff(now(), last_access)) as duration from session_log where remote_ip='$remote_ip' and session_id=(select max(session_id) from session_log where remote_ip='$remote_ip');", $con)  or die(mysql_error());
if(mysql_num_rows($res) == 0) {
print("<center><h1>Invalid Session!</h1><br />");
mysql_close($con);
print("<a href="dbloginform.html">Go to Login Page</a></center>");
exit(0);
}

$session_id = mysql_result($res,0,"session_id");
$uid = mysql_result($res, 0, "user_id");
$d = mysql_result($res,0,"duration");
$n=mysql_num_rows($res);
$st=mysql_result($res,0,"status");
if($st == "EXPIRED") {
print("<center><h1>Invalid Session!</h1><br />");
mysql_close($con);
print("<a href="dbloginform.html">Go to Login Page</a></center>");
exit(0);
}
else if($d > $time_out) {
print("<center><h1>Session timed out!</h1><h3>" . ($d/60) . " minutes after last access!<br />(time limit is " . ($time_out/60) . " minutes)</h3><br />");
$res = mysql_query("select @m:=max(session_id) from session_log where remote_ip='$remote_ip';", $con);
$res = mysql_query("update session_log set status=2 where remote_ip='$remote_ip' and session_id=@m;", $con)  or die(mysql_error());
mysql_close($con);
print("<a href="dbloginform.html">Go to Login Page</a></center>");
exit(0);
}

mysql_query("update session_log set last_access=now() where remote_ip='$remote_ip' and session_id=$session_id;", $con)  or die(mysql_error());
$res=mysql_query("select name from user where id=$uid;",$con);
$user=mysql_result($res, 0, "name");
mysql_close($con);
return true;
}

if(checkStatus()) {
printf("<center><b>Welcome %s! Your id is: %s</b><br />",$user,$session_id);
printf("<a href="dblogout.php">Logout %s</a></center>",$user);
}
?>

The script starts working from the if condition found at the bottom of the script. The status checking script verifies whether the session already expired or whether had timed out. An appropriate message is displayed for both the cases. If the session is valid, the script sets the last-access time on the session_log table to the current time.

Figure 3: Status check for valid session
Figure 3: Status check for valid session

Figure 4: Status check for timed-out session
Figure 4: Status check for timed-out session

Protecting content using a server-side session

Once the login, session status and logout scripts are created for server-side session management, protecting the pages is similar to cookie-based sessions (discussed in last month’s issue). Calling require_once('dbloginstatus.php') at the top of the protected pages ensures that only authorised persons view the content:

<?php
/*dbprotectedimage.php*/
require_once(‘dbloginstatus.php');
print("<div align="center"><img src="summer.jpg" width=75%></div>");
?>

The protected page for a valid session is shown in Figure 5 and that of an invalid session is shown in Figure 6.

Figure 5: Protected content (dbprotectedimage.php) for a valid session
Figure 5: Protected content (dbprotectedimage.php) for a valid session

Figure 6: Protected content for an invalid session
Figure 6: Protected content for an invalid session

Logging out from a server-side session

The session can be explicitly terminated by calling the dblogout.php URL, which sets the status to EXPIRED (or the numeric value 2). After the dblogout.php script is called, entering any protected page is not permitted. Figures 7 and 8 show the logout screen and the message displayed when calling dbloginstatus.php after completing the session. The dblogout.php script is listed below:

<?php
function logoutUser() {
$uid;
$time_out=120;
$user;
$valid=false;
$remote_ip = $_SERVER[‘REMOTE_ADDR'];
$con = mysql_connect('localhost','user','pass') or die(mysql_error());
mysql_select_db("session", $con) or die(mysql_error());
$res = mysql_query("select user_id, session_id, status, (now()-last_access)/60 as duration from session_log where remote_ip='$remote_ip' and session_id=(select max(session_id) from session_log where remote_ip='$remote_ip');", $con)  or die(mysql_error());
$session_id = mysql_result($res,0,"session_id");
if(mysql_num_rows($res) == 0 || mysql_result($res,0,"status") == "EXPIRED") {
print("<center><h1>Invalid Session!</h1><br />");
mysql_close($con);
print("<a href="dbloginform.html">Go to Login Page</a></center>");
exit(0);
}
else if(mysql_result($res,0,"duration") > $time_out) {
print("<center><h1>Session timed out!</h1></center><br />");
$res = mysql_query("select @m:=max(session_id) from session_log where remote_ip='$remote_ip';", $con);
$res = mysql_query("update session_log set status=2 where remote_ip='$remote_ip' and session_id=@m;", $con)  or die(mysql_error());
mysql_close($con);
print("<a href="dbloginform.html">Go to Login Page</a>");
exit(0);
}

$uid = mysql_result($res,0,"user_id");
$res = mysql_query("select name from user where id=$uid;", $con)  or die(mysql_error());
$user = mysql_result($res,0,"name");
$res = mysql_query("update session_log set status=2, last_access=now() where remote_ip='$remote_ip' and session_id=$session_id;", $con)  or die(mysql_error());
print("<center><h1>Successfully logged out $user!</h1><br />");
print("<a href="dbloginform.html">Return to login page</a></center>");

$res = mysql_query("update session_log set status=2 where (now()-last_access)/60 >= $time_out;", $con)  or die(mysql_error());
mysql_close($con);
}
logoutUser();
?>

Figure 7: Logout message
Figure 7: Logout message

Figure 8: Status message after logging out
Figure 8: Status message after logging out

The logout script sets the status of the session to Expired and the last-accessed time to the current time. If the session was already closed or timed out, the logout script will display the appropriate message and terminate. Figure 9 shows the entries in the session_log table. This table would serve both as a record of the login/last-accessed times for users as well as a safe mechanism to hold current session details.
Figure 9: Session log maintained by the server
Figure 9: Session log maintained by the server

Pros and cons of server-side sessions

Forming sessions from the server side provides the safest strategy for the formation of sessions. The formation of sessions is fail-safe, since it is completely independent of cookies. Normally, server machines are better protected from intrusions. Hence, the chance of unauthorised people getting hold of session details is very limited. However, it takes a bit more coding effort on the part of the programmer.

After getting through the client- and server-side session management strategies, I’m sure you now will be able to easily protect Web content from the eyes of unauthorised users in your projects. Although server-side sessions are a bit tedious and time consuming, the results are more rewarding than the cookie-based implementation.

8 COMMENTS

  1. I disagree with the author. This method is not secure at all since it is based in the users IP address. If you have several users using the same IP address (comig from the same proxy/firewall) the script will assume that all of them are logged in (i.e. all users within my company appear to have the same IP address to http://www.yahoo.com since we all use the same proxy).

    I consider cookie authentication a better choice.

  2. Invar, I think this article describes a better way of securing the session. Just use the IP address in combination with the client username

  3. @V. Nagaradjane

    How can i restrict the user who is already loged in another machine?

    The case is suppose a user A is loged in machine A. At machine B if user B tries to login with credentials of user A then that user B will get an error message that “Your session is already active.”

    For this i have store a session id for user A in db whenever he logs in.

    Now to solve this case i am searching for way to fetch the status of session id. If the session id is active on server then i will restrict user B to login.

    Is there any one to help me for searching this.

LEAVE A REPLY

Please enter your comment!
Please enter your name here