CGI and Perl

CGI Security

If you use CGI scripts at all in your Web server, you'll need to take special care to make sure that they're safe. There have been a number of break-ins and other security violations due to incorrectly configured or carelessly written CGI programs. It will pay to be completely familiar with all of the issues and precautions regarding using the CGI.

General Considerations: Dos and Donts

Because the CGI modules are introduced later in this tutorial, you will not learn how they specifically deal with the potential security holes at this time. Most security holes introduced through the use of CGI scripts are unintentional; the following section discusses some of the most pop- ular ones. Never Trust Anything Sent As Input This is the first rule of safe CGI programming. Just because the server is running as the nobody user doesn't mean that malicious activities can't be carried out. If your input data is being processed in any way, even just writing it onto a file, the potential for an attack exists. The following list illustrates some of the most important considerations when creating CGI programs and handling input to forms:

Variable Length A form may prompt for a variable specifying a MAXLENGTH, but the sender may potentially send a form variable thousands of bytes long. This problem was, in fact, a CERT Announcement against an early version of the NCSA httpd. Be sure that you're not running httpd with a version number earlier than 1.4 to avoid the hole this created. Also make sure that your script itself can handle such a long submission properly.
Shell MetaCharacters The input from a form could potentially include shell meta characters or other special characters or commands that could get you in trouble.


Never, under any circumstances, pass user input to the shell in raw form; in fact, you shouldn't have to ever invoke a shell process at all if you're doing things right. If you use the CGI and Mail modules to process the form and perform the desired action(s), this is handled for you automatically. Even so, it never hurts to apply your own standard to the returned values from the CGI parsing process just to be sure.

Selection Lists/Variables The value input for some selection list item, or any variable for that matter, may not be among the ones that you've specified. If you have a default action, make it do nothing or warn for any unexpected value.
Hidden Variables Don't assume that just because you define a variable to be hidden in your form, it can't be seen by any means. The same goes for Netscape cookies: The malicious client could send something to you as a hidden variable or cookie that you didn't give the client in the first place.

In general, as has been mentioned, invoking a shell with any form input should be avoided. It is very easy to do the right thing with user input when you use the CGI modules to process the form. Alas, there will always be the need for the quick hack, sometimes due to urgency or immediate need for results, and other times due simply to laziness. A quick review of the no-no's is thus in order:

  1. Never invoke the shell directly. Perl provides several techniques for getting past the shell directly to the desired program, and these should be utilized. For instance, you could invoke sendmail by using the following:
    system("/usr/lib/sendmail -t $address < $input_file");

    But doing it this way is a big mistake, especially when the $address and/or the $input_file variables may come from the form itself. It's much better to invoke sendmail like this:

    open(SENDMAIL, "/usr/lib/sendmail -t|");

    Then print directly on the SENDMAIL pipe, first the header, followed by the body of the mail, and close it. However, it can't be stressed enough that even this could get you in trouble. You should always use the Mail::Send module's methods to send mail. Another technique for getting past the shell in system() is to use the somewhat obscure technique of quoting each parameter to the command line:

    system "/usr/bin/sort", "$input_file";

    This syntax invokes the sort command directly instead of passing the arguments through the shell. Note how we explicitly specified the full path to the sort command. Never rely on the $PATH variable to figure out which command you really mean. This is how Trojan horses get inside the gates. Also, don't forget that system() isn't the only way to invoke a shell with Perl. There are also the backtick operators, fork(), exec(), and pipe(), and even glob() and implicit glob via <'command'>. Be very careful with quoted eval"" statements, as well.

  2. Always turn tainting on with the -T option to Perl. This option automatically renders arbitrary input as "tainted" and unusable in subprocess invocations until it is untainted. Of course, it's quite easy to untaint variables, but if you turn on -T in the shebang line of your Perl script, you might catch yourself in a moment of weakness when you've forgotten to escape some arbitrary input.

  3. If you insist on parsing your own input, there are basically two policies, the first being that anything not permitted is forbidden. The regular expression
    $address =~ /^[\w@\.\-]+$/;

    allows only a specific class of characters, which should match an e-mail address. If, when your $address variable doesn't match this regexp, your programs errors, you're enforcing this policy. The other alternative is to permit anything that isn't forbidden. If, for instance, you check your $address for any shell meta-characters with the following regexp:

    $address =~ /([;<>\*\|'&\$!#\(\)\[\]\{\}:'"])/;

    and then allow anything that doesn't match, you're enforcing the less-restrictive policy, specifying only what is unsafe. As you might expect, the former is preferred. And again, using the CGI modules handles this for you.

  4. Finally, always be sure to check and double-check before allowing arbitrary input to be used for a pathname. Be especially vigilant for the old `..' operator, which can be used to access files down from the htdocs directory and possibly into the /etc directory, where the passwd file lives.

    Using the various CGI modules to create and especially process your form input will help you to avoid these traps. You have additional power to prevent attacks and limit the capabilities of the client using several other powerful Perl modules that aren't discussed elsewhere in this tutorial. Let's take a look at these now.