Sessions with perl over Apache

30/05/2005

The goal is to handle web sessions over an Apache server with perl. Once the user is authentificated from a form, we give him a session cookie and we store data about his session on the server in variables.

Identification

XHTML form

It all begins with a form that would look like this :

<form action="/cgi-bin/login.cgi" method="post">
<fieldset id="identification">
<legend>Identification</legend>
<dl>
<dt><label for="login">Login</label></dt>
<dd><input type="text" name="login" id="login" size="10" /></dd>
<dt><label for="pwd">pwd</label></dt>
<dd><input type="password" name="pwd" id="pwd" size="10" /></dd>
</dl>
<p><input type="submit" value="Login" />
<input type="submit" value="Logout" name="logout" /></p>
</fieldset>
</form>

Once style, here's what it'll look like :

Identification

This is a simple XHTML strict, validated and accessible form. The user enters his login and password that gets submited. There's also a button to logout.

Identification script

We are going to use the CGI::Session module for handling sessions are we won't use CGI::Cookie but we'll write cookie directly in order to reduce dependencies. Here we'll do simple identification but if had more users or a complex hierarchy to deal with, we would use an ash table stored on the local server (use SDBM_File; then tie(%pwd, 'PWD_file', 'c:/apache/dbs/pwd.sdbm', O_RDWR|O_CREAT, 0666); and in the end untie %pwd;) or an XML file stored out of the public zone of the server. We don't give the cookie any date so that it expires automatically once the browser window is closed.

#!perl
# All by HAbeTT
# Login identification

use CGI::Session;
use CGI;

# Get the CGI form data
$query = new CGI;

if ($query->param('logout')) {
  # Logout part
  # Get the session id
  $cookie = $query->cookie(-name => "session");
  if ($cookie) {
    CGI::Session->name($cookie);
  }
  # Expire the server session
  $session = new CGI::Session("driver:File",$cookie,{'Directory'=>"d:/apache/tmp"}) or die "$!";
  $session->clear();
  $session->expire('+2h');
  # Remove the session cookie
  print "Set-Cookie: session=$id; domain=.$host; path=/; expires=Sat, 8-Oct-2001 01:01:01 GMT\n";
  # Goes back to the identification page
  print "Location: ".$ENV{'HTTP_REFERER'}."\n\n";
  exit(0);
}

# Initiate the session
$session = new CGI::Session("driver:File",undef,{'Directory'=>"d:/apache/tmp"});

# Fetch login and password
$login = $query->param('login');
$login =~ s/(?:\012\015|\012|\015)//g;
$pwd = $query->param('pwd');
$pwd =~ s/(?:\012\015|\012|\015)//g;

# Check the credentials and populate the status variable
if (($login eq 'Admin') and ($pwd eq 'Password')) {
  $zstatus = 'administrator';
} elsif (($login eq 'Private') and ($pwd eq 'Soldier')) {
  $zstatus = 'private';
} else {
  print "Content-type: text/plain\n\nNope !";
  exit(0);
}

# Write the session variable on the server
$session->param('status',$zstatus);
$session->expire('+2h');

# Send the cookie linking the user to the server session
$id = $session->id();
$host = $ENV{'HTTP_HOST'};
print "Set-Cookie: session=$id; domain=.$host; path=/\n";

# Goes back to the main page
print "Location: ".$ENV{'HTTP_REFERER'}."\n\n";

# Clean the server
if (int(rand(10)) == 1) {
  # expire old sessions
  $filez = "d:/apache/tmp/*";
  while ($file = glob($filez)) {
    @stat=stat $file; 
    $days = (time()-$stat[9]) / (60*60*24);
    unlink $file if ($days > 3);
  }
}

exit(0);

Using the server sessions and variables

In the scripts where we need the stored data, we'll just need the following lines of code :

use CGI::Session;
use CGI;

$query = new CGI;
$cookie = $query->cookie(-name => "session");
if ($cookie) {
  CGI::Session->name($cookie);
}
$session = new CGI::Session("driver:File",$cookie,{'Directory'=>"d:/apache/tmp"}) or die "$!";
$status = $session->param('status');

You are free to store as many session variables as you want on the server. This strategy is much safer and respectful than storing all the information inside user cookies because you keep the hand over all the data. If a malicious user finds by chance (or gift) a valid session id, it won't be of much use because the session will expire anyway.

main menu