Copy Link
Add to Bookmark
Report

The Discordant Opposition Journal Issue 11 - File 8

Perl CGI Authentication 101

by cronus [cronus@whitedust.net]


I'd like to begin by thanking kleptic for having more foresight than the rest of us with regard to the DoJ. I am very excited to see whats to come from the new DoJ camp in the future.

If there is any one side of programming that I have had to revisit several dozen times in terms of perl programming, it has to be CGI authentication. I am assuming the reader already understands the rudiments of perl programming as well as CGI programming.

The first problem that you are likely to encounter when trying to code an authentication aware CGI script is that the HTTP protocol is stateless. This means that between each page displayed (or between each click of the user) no information is passed on. It makes little sense to have a user enter their username and password on a login page only to have those details forgotten on the next page. You really don't have many weapons at your disposal for developing a statefull setup. Cookies, in one form or another, are going to support the basis of your authentication system.

When dropping a cookie it must be output to the user's browser before the actual Content-Type header line. I've included a nifty little perl function, of my own creation, at the end of the file called setCookie that allows you to pass it the cookie details to drop as well as a timeout in minutes. I've included that timeout facility because although it possible to drop cookies that are called Session IDs - I have yet seen a browser that handles the spec on them properly.

If you intend to save the user's password in a cookie then you should at least realise it should be encrypted to protect it from prying eyes. Its been my experience that its not worth the hassle of actually encrypting the password, rather it makes sense to make a one-way hash of it instead. Perl running on a unix system will have access to the crypt function which will return a hash of the password you pass to it. I don't claim to have an understanding of cryptography but the process of creating a one-way hash ensures that the string, or hash, generated will always be the exact same for the same password and salt. But the one-way hash is, as its name points out, a one way function and cannot be reversed. I've also included some sample code that uses the crypt function at the end of the file for your perusal.

If you are looking for some method of storing usernames and passwords safely and securely, I recommend you look at MySQL for unix machines (www.mysql.org) which is a free database package. To access it from perl you will need to search CPAN for DBD and DBI-mysql modules. It would, unfortunately, take another article to cover it fully. Perhaps next issue ;]

An alternative to pass the user's password back and forth is to check the username and password once and then pass an authentication ID to the user as a cookie that you also store locally, perhaps in a database. Then all you need to do for later authentication checks is to compare the ID passed back to your script from the cookies to the IDs in the database and you have yourself a statefull connection. This is not only safer but generally faster is done right.

I've mention security already a few times, perhaps I should expand on what I mean. Its very easy to determine if a user has entered a valid username and password, but its quite a bit more complex to ensure that no one can use those details who shouldn't be able to. Never transmit passwords in plaintext when they can be encrypted or hashed as I've explained. An SSL connection is preferable to a plaintext HTTP connection. There isn't even any need to send a plaintext username when you could just use some sort of authentication token that is meaningless to anyone who doesn't have access to your database.

Any script that a user has access to after they have logged in should first check thier authentication credentials before doing any other work. Every function of your finish masterpiece should check the user's details before doing anything.

I think thats enough of the basics. If I've left you with any questions please email me at cronus@whitedust.net otherwise go and program.

#$crypted_password = crypt_pw($REAL_PASSWORD); 
# Would generate a crypted password that you can safely use in a
# cookie.
#$auth = check_pw($CRYPTED_PASSWORD, $REAL_PASSWORD);
#if ($auth == 0) { die "Login failed...\n"; }
# Would preform a check on the crypted password previously stored
# in a cookie.
sub crypt_pw {
$password = shift;
my $salt = join '', ('.', '/', 0..9, 'A'..'Z','a'..'z')[rand 64, rand
64];
$pw = crypt($password, $salt);
return $pw;
}
sub check_pw {
($crypt_passwd, $real_passwd) = @_;
$pw = crypt($real_passwd, substr($crypt_passwd, 0, 2));
if ($pw eq $crypt_passwd) { return 1; }
return 0;
}

#setCookie("Username","cronus","30");
# Would drop a cookie (Username) with the value cronus that would
# expire in 30 minutes. You should remember to change the path
# and domain values.
sub setCookie {
($name, $value, $min);
$min = $min * 60;
$gmt = time + $min;
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) =
localtime($gmt);
($junk,$year) = split(/0/, $year);
$year = "0" . $year;
@months =
('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
$mon = $months[$mon];
@days =
('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday');
$wday = $days[$wday];
$hour = sprintf("%02d",$hour);
$min = sprintf("%02d",$min);
$sec = sprintf("%02d",$sec);
$date = "$wday, $mday-$mon-$year $hour:$min:$sec GMT";
print "Set-Cookie: Username=$F{'Username'}; expires=$date; path=/;
domain=.whitedust.net\n"
;
}

← previous
next →
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT