ACCC Home Page ACADEMIC COMPUTING and COMMUNICATIONS CENTER
Accounts / Passwords Email Labs / Classrooms Telecom Network Security Software Computing and Network Services Education / Teaching Getting Help
 
CGI Programming at UIC
0 Contents 1 Introduction 2 Background 3 Perlwrap 4 Perl 5 Examples A1 Related Links

5. Examples

 

These examples do represent a range of issues involved in CGI programming using perl. I hope they are useful in getting started.

They are in no sense complete, and make no effort to deal with issues of perl that do not relate somehow to CGI. Caveat Emptor

 
   
 
     
Example 1 -- Trivial Case
  This script simply prints out 'hi'. Two things to note:
  • The output of the script starts with: Content-type: text/plain, followed by a blank line. This is essential, so that the browser will know what mime type is being returned. And without this, the browser will not see the rest of your output, but will get an error message.
  • The url. This url corresponds to a file on tigger, whose full path is /homes/home8/adabyron/cgi-bin/example1.pl
 
     
Example 2 -- Authentication
  This script shows how to force the user to use bluestem authentication. Important points:
  • The authenticated netid appears in the $ENV{REMOTE_USER}
  • The url must start with https, and you must use perlwrap-auth.
  • If the user tries to circumvent authentication, then $ENV{REMOTE_USER} will not contain a value. The script can take appropriate action, either by issuing an error message or by issuing a redirection.
  • Using the variables $ENV{SERVER_NAME} and $ENV{SCRIPT_NAME} to reconstruct the url is good programming. Every now and then, you may copy a script or move it to a new location. This code is nicely portable.
 
     
Example 3 -- Debugging I
  This script is broken on purpose. It left out the Content-type directive, which is required by HTTP.
  • The error message when a user tries to run this script is not particularly enlightening.
  • Note the information you get in debug mode. In this particular case, the script actually runs (i.e. there is no perl error), it just puts out bad HTTP. You'll have to figure this out, but debug mode sure helps. The clue is in the last line, where there is no Content-type
 
     
Example 4 -- Debugging II
  Once more, we try a bad script to examine how to debug. This time, we use a script with a perl error.
  • Note, again, that the server error message is not very helpful to you or your users.
  • When run in debug mode, both STDOUT and STDERR are returned to your browser. In this case, you can get the perl error and fix the problem. I'll leave it as an exercise to the reader to fix this perl problem.
Sometimes you can fix perl errors just by running the script from the command line, rather than through perlwrap. This doesn't always work, because the web server environment is different than the command line environment, and the script may behave differently.
 
     
Example 5 -- Dealing with input
  This example shows how to deal with user input, either from the url, or from a form. In particular, this script has a dual purpose -- if it receives good input (either from the url or a form), it prints out what it received. But if it doesn't receive input, it prints out a form that will submit values back to the same script. I often like to code this way, so that the input form and the input handling program reside in the same file.
  • I used CGI.pm to parse the input. This hides various complications, and I highly recommend it. CGI.pm does vastly more than this simple example, although I rarely have need for too much more. You should check out the details by logging onto tigger or icarus and typing perldoc CGI.
  • This particular invocation of CGI.pm doesn't care if the input was from a GET request (i.e. in the url, or sometimes from a form) or from a POST request (from a form).
  • Note again the use of $ENV{SCRIPT_NAME} to make a self-referential url.
 
     
Example 6 -- Taint Mode
 

Here we start dealing with security concerns, so listen up! This example runs the 'ls' command, to search the local directory for files whose extension is supplied by the user.

Running a command through the shell is always a little dangerous, because you have to be very sure you run the command you want to run. This seems like a trivial statement to beginners, but experts and hackers know how to take advantage of naivete.

When the command is based on user input, the danger increases significantly. This is where taint mode comes in. It forces you to examine user input, and to sanitize it, before using it to influence anything outside your program. In this case, you aren't trying to influence anything outside your program, but the call to ls does involve the shell, and therefore could damage things, so perl takes special care.

Here, the sanitizing is quite strong. I use:

         $extension =~ /^(\w{1,10})$/;
         $extension = $1;
This means that the variable $extension can only contain letters -- no spaces, no punctuation, no oddball characters. And I know, from the use I intend to put this to, that mere letters will not harm me.

In fact, I have been so cautious in this example, that I will only accept between 1 and 10 letters. That's probably overkill here, but it does indicate that I have thought very carefully about the form of input I deem valid. And that is key -- you must decide what is valid input. If you accept any old input, what happens if I construct a url like: ....example6.pl?ext=pl;rm%20%2Dr%20%2A (Kids, don't try this at home. It could erase all your files. Just remember, a hacker can try out any url he wants. He's not limited to the links or forms you provide. Never assume that input to a script comes from the form you provided!

  • If you influence things outside your script (i.e deal with files or invoke the shell), perl will force you to de-taint any user input. Don't try to outwit perl -- give some thought to what constitutes valid input, and do not accept any more than you have to. Be extremely careful of accepting punctuation or spaces. Of course, this advice depends strongly on exactly how you use the input. Writing a user's input into a file is much safer than using that input to determine a file name. De-taint sensibly.
  • Whenever you invoke an outside command (e.g. ls in this example), be sure to specify the full path (e.g. /bin/ls). As a further precaution, set the variables $ENV{PATH} and $ENV{ENV} either to '' or to values that you know are safe. Outside commands can be invoked in many ways, including backticks, calls to fork(), system(), exec(), or open().
  • Never assume a hacker has to use your links or forms. A hacker can construct any input he wants, and can send it to your script. Just because you use a select box in a form doesn't mean a hacker can't put in his own options. Program defensively.
 
     
Example 7 -- Writing Files
 

Writing files from a CGI script is no different than from any other program. However, I have an example to illustrate file locking. It's quite possible for a CGI script to be invoked several times simultaneously, and for each of these invocations to write to the same file. However, if you are careful to lock and unlock the file properly, this is not a major problem.

  • Use the full path, for both commands and files.
  • Check return codes. Don't assume that everything works all the time.
  • Keep the output file locked as little as possible.
 
     
Example 8 -- Writing DBM Files
 

This is really the same example as before, but I use a DBM file instead of an ascii file. If you don't know what a DBM file is, and how to access them via perl's tie function, just skip this example. The only reason to include this example is that tie'd DBM files are a bit trickier to lock than ascii files.

 
 

CGI Previous: 4 Perl Next: A1 Related Links


2005-8-16  wwwtech@uic.edu
UIC Home Page Search UIC Pages Contact UIC