I would think there would be a FAQ on how to give your
CGIs access to write local files while minimizing security
risks. But
The Idiot's CGI Guide didn't mention this and pointed
to
The WWW Security FAQ, which also didn't mention it
(that I could see).
So here are some things I consider important:
- Pick a specific directory where your CGI will write files.
- Don't allow this directory or files in it to be served.
You can do this any of the following ways, though only some
of these will also satisfy the other criteria, depending on
your environment.
- Locate the directory outside of the document tree.
If your web server chroot()s, then you'll need a directory
under the chroot() location but outside the document tree.
- Configure the web server to not serve that directory.
Don't rely on this method alone.
- Protect the directory from the web server. If your CGI
runs in a wrapper that changes the UID that it runs under,
then configure the directory so that it is readable but
not executable to your CGI UID and is neither readable nor
executable to the web server (nor the world).
- Hide the directory from the web server. This is a last
resort, but is a nice small layer of extra security when
used with one or two of the previous methods. Find a directory,
"the parent", that has a default
file (such as index.html). Deny the web server execute
access to the directory (but you must give it read access).
Create a subdirectory with an obscure name, "the sub", in
the parent. Give the web server read but no execute access
to the sub. Now the web server can access the sub and files
in it, but only if an attacker can guess the name of the sub
and the name of the file. Not much security, but it is
better than giving the attacker write access to the directory
that your script is in.
- Give your CGI and only your CGI write access
(and read access but no execute access) to the directory.
Note that this prevents your CGI from being able to list
what files are currently in that directory. You can test
for the existance of a specific file.
If your CGI runs as "nobody", then I'd just abandon the
whole idea and find another server since "nobody" can only
write to a directory if the world can write to it.
- Don't trust the data you read from this directory.
Turning on Perl taint checks will help you remember this.
Just because your CGI never writes "rm -f /" to any of
these files, don't run $l=<FILE>;system($l);.
If, like many of us, asking questions of your web server
administrator is difficult, you can figure out a lot about
your server configuration with some experiments. Let's
assume that your user name is "joe", the root of the web
tree or subtree that you have control over is "~/webroot",
it is served as "http:://www.x.com/~joe", your CGIs go in
"~/webroot/cgi-bin", and they are served as
"http://www.x.com/cgi-bin/cgiwrap/joe/script.pl".
cd ~/webroot
chmod u=rwx,go=rx .
mkdir test
cd test
chmod u=rwx,go=r .
echo "<html><body>Nothing here.</body></html>" >index.html
chmod ugo=r index.html
mkdir hades
chmod u=rw,go=r hades
cd ..
mkdir cgi-bin
cd cgi-bin
chmod u=rwx,go=rx .
Now you can put test scripts in your cgi-bin directory
and figure out if your server chroot()s, what UID your
CGIs run under, etc.
print "Content-type: text/html\r\n\r\n<HTML><BODY><PRE>\n";
print "$< $> $( $) $^X $] $0\n";
print join(":",getpwuid($<)),"\n";
print "$ENV{PATH}\n";
print `/bin/pwd`; # Not for Win32
#OR# print Win32::getcwd(),"\n"; # For Win32
print "</PRE></BODY></HTML>\n";
exit(0);
Then you can try creating files:
print "Content-type: text/html\r\n\r\n<HTML><BODY><PRE>\n";
if( ! chdir("~joe/webroot") ) {
print "Can't chdir to ~joe/webroot: $!\n";
} elsif( ! open(TEST,"> hades/emptytest",0777) ) {
print "Can't create emptytest: $!\n";
} else {
close(TEST);
}
print "</PRE></BODY></HTML>\n";
exit(0);
Once you get files created, check the ownership and
permissions on the created files to double check how
your CGIs are being run, for example, what umask is set.
If you don't have shell access, then chmod
via FTP will probably have to be run as
quote site chmod (use quote help
to check this).
|