Print this Article (NS4)
Netscape Navigator
Internet Explorer
Opera
Neoplanet

Forums
HTML
General
Site Dev
Programming
Flash
Grafix (Art)

Laboratory
Smart HTML
Color Lab
Generators

Contents
Simple CGI
  1. Hello World
  2. Print function
  3. Quoting, EOF
  4. Metacharacters
  5. Special Characters
Perl Basics 1
  1. Variables
  2. Arrays
  3. Hashes
  4. Split function
  5. Subroutines
  6. Defaults
Form CGI
  1. Loops
  2. Conditions
  3. Boolean Statements
  4. Pattern Matching
Time CGI
  1. Local Time
  2. GM Time
  3. time function
Perl Basics 2
  1. Reading Files
  2. Writing Files
  3. Including Files
  4. chop function
  5. chomp function
Guestbook CGI

Redirect CGI

Poll CGI

  1. Giving Commands
  2. Voting
  3. Results Display
  4. Adding Your Vote
Password CGI
  1. Authentification
  2. Multiple Users
  3. Encryption
Mailing List CGI
  1. Sendmail
  2. Multiple Recipients
Unlimited Subdomains CGI

News Grabber CGI

  1. LWP::Simple
Message Board CGI (Part 1)

Back to the Top


Perl Case Study - Password CGI
By Lisa Hui

There are many reasons to restrict access to a script or web page. Of course there's no 100% secure way to do it, even with a Secure Socket Layer (https), but in most cases, a simple password routine in your CGI script will do. In theory, how does this work? A user would input a password into a field (for accounts you usually have to enter in a username and a password). Then the CGI script would take this password and compare it to a predefined value that is either stored in the script itself, or in a file.

The process:

1.Get the the inputted password
2.If the value is not predefined in the script (multiple users), open up the file and store the password listed in it as a variable.
3.Compare the two values. (Do any translation if necessary.) If the input and the variable are equal, then print out a page to the browser saying that the user has been authentificated - this is called password verification. Else, print out a message saying that one or the other was incorrect.

Modifications for use:

1.Encryption - sometimes you may want to implement a simple encryption; just realize that this is easily crackable by experienced "hackers," however, setting a directory unreadable to the browser will allow you to avoid password stealing (although server side includes might still cause a security problem).
2.Change what the script does if the password is verified. Possibly, print out the password-protected data to the browser window.


Getting the Input

We can use our Form CGI case study material to handle the input (this kind of application seems to be very flexible for handling various script tasks). So we get our information through a form (or through the URL's GET method). Let's say that the input fields were named "username" and "password"; the data inputted would be stored in $FORM{'username'} and $FORM{'password'}.

Authentification/Validation

Let's start with the simplest case: the correct username and password previously defined in the script itself.

#!/usr/local/bin/perl
#################
# File Name: password-simple.cgi
# Simplest version

$correctUsername = "adinfinitum";
$correctPassword = "web";

print "Content-type:text/html\n\n";

#################
# Form Data Parsing

if ($ENV{'REQUEST_METHOD'} eq 'GET') { $buffer = $ENV{'QUERY_STRING'}; }
else { read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); }

@pairs = split(/&/, $buffer);

foreach $pair (@pairs) {
   ($name, $value) = split(/=/, $pair);
   $value =~ tr/+/ /;
   $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
   $FORM{$name} = $value;
}

################
# Comparison

#Use a nested if/else statement like the one below to make debugging
#much easier
if ($FORM{'username'} eq $correctUsername) {
   if($FORM{'password'} eq $correctPassword) { print "You've passed the test!\n"; }
   else { print "Password Incorrect.\n"; }
}

else { print "Username Incorrect.\n"; }


See this script in action: password-simple-test.shtml

Multiple User Authentification

The above is sufficient if you need only one set of username|password. Let's make it a tad more resourceful by getting the data from a file - and this time we should be able to maintain more than one set (perhaps to personalize). I've named this supplementary password list file passwd.txt.

Check it out; you'll notice that I've entered three sets already. You can, of course, add as many as needed :) It may become resource intensive if there are over 5,000-10,000 entries, but otherwise, it works abundantly well.

What you need to do:

1.Get the list from the file.
2.In a loop, cycle through each list element (line), split them into $username|$password pairs, and match them up with the input from the form: $FORM{'username'} and $FORM{'password'}. If they match up, then exit the loop by using the last operator. If they don't match, then print out an error message.

#!/usr/local/bin/perl
#################
# File Name: password-simple.cgi
# Simplest version

print "Content-type:text/html\n\n";

#################
# Form Data Parsing

if ($ENV{'REQUEST_METHOD'} eq 'GET') { $buffer = $ENV{'QUERY_STRING'}; }
else { read(STDIN, $buffer, $ENV{'CONTENT_LENGTH'}); }

@pairs = split(/&/, $buffer);

foreach $pair (@pairs) {
   ($name, $value) = split(/=/, $pair);
   $value =~ tr/+/ /;
   $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg;
   $FORM{$name} = $value;
}

################
# Comparison

open(LIST, "passwd.txt");
  @pass = <LIST>;
close(LIST);

foreach $line(@pass) {
   chomp($line);
   ($correctUsername,$correctPassword) = split(/\|/,$line);
   if ($FORM{'username'} eq $correctUsername) {
      if($FORM{'password'} eq $correctPassword) { print "You've passed the test!\n"; last }
      else { print "Password Incorrect.\n"; }
   }
}

Notice that this "upgrade" has only a few extra lines of modifications. You basically have the idea of how this works if you've followed along so far; it the comparison that makes the difference. Now how about some of those extra features?

Encryption

We have the option to encrypt a username and/or password using the crypt function. It will return the value of the encrypted word or phrase, using the following syntax in the script:

$encryptedPassword = crypt($password,$salt);

What is $salt, you ask? It is simply a "seed" value, consisting of 2 characters (i.e. 2 alphanumeric values), that is used in creating the encryption. You could simply have $salt be something you define in the script, but that's a bit unsafe since your encrypted password would always start with those same two letters. See for yourself experimenting with our simple encryption testing device. [You can also use this to manually encrypt passwords for use in this example :)]

Now that you have encrypted them, do you decrypt them before comparing them to the ones inputted through the form? Nope, that would be a bit more complicated than we need to make it. Simply encrypt the inputted username and password and compare those to the already encrypted values in the file. But what about decryption - can it be done? Yes, but to keep on the safe side, we can live without it; it may not be lawful in some countries to present a method that allows people to decrypt too! *Wonders why*

Perhaps, it's safer to let the script decide what the salt is. In our example, let's just take the first two letters of the inputted values (username and password) and use that as the salt to decrypt each respectively. To do this, we can use another function called substr, which refers to substring (taking only a part of a string - after all, what is a string other than an array of characters?). It basically returns a substring from a given expression:

$substring = substr($expression,$offset,$length);

Offset denotes how far from the beginning of the string to begin the substring. Length denotes the length of the substring (number of characters) to return. If you omit the length value, it will return everything after offset to the end of the string.

For example, let our expression be "hello".

$expression = "hello";

Then substr($expression,0,1); would return "h" since you specified an offset of zero and only one character from the string to be returned.

$substring = substr($expression,1,1);
#What you get: $substring = "e";

$substring = substr($expression,0,2);
#What you get: $substring = "he";

$substring = substr($expression,1,2);
#What you get: $substring = "el";

Offset and length can have negative integer values as well! What would that do? A negative offset would be taken to mean that the script is to count the offset from the end of the string, not the beginning which is what a positive integer value would do.

$substring = substr($expression,-3,1);
#What you get: $substring = "e";

A negative length value would tell the script to leave that many characters off the end of the string.

$substring = substr($expression,0,-2);
#What you get: $substring = "hel";

Back to why this is useful: we can take a substring value from the original input and use that to encrypt the data:

$encryptedPassword = crypt($password,crypt($password,0,2));

In the above line, crypt would return the first two letters of the password as the salt for the crypt device. So if it is encrypted, don't forget to encrypt the inputted values before trying to compare :)

Last Updated August 3, 1999


Perl Case Study - Mailing List CGI

©1999 Team 26297 "Ad Infinitum Web." All rights reserved. Any reproduction of this document for commercial or redistribution purposes without the permission of the author is forbidden.