CGI: Safety First

A practical guide to safe CGI.
Good Things

It's time for something less depressing. Can you do it right? Sure you can—it just takes a little bit of effort. The problem is you don't really want your CGI scripts to be executed by a stranger like www. You want to have your scripts running as yourself! For the execution of programs as another user, suid and sguid were invented.

If you make a program suid (or sgid), you run it as the user (or group) who owns the file. That was exactly what you wanted in the first place. So you make your scripts suid. And still under Linux it doesn't work. Why? Because suid scripts are insecure and the Linux kernel refuses suid scripts altogether (look at the comp.os.unix FAQ for some stunning ways to become root).

But suid programs are safe to use. It only means that you have to write a wrapper, a little C program, counter.c, which you make suid:

#include <stdlib.h>
void main(int argc, char *argv[])

Does this suid stuff always work? No—many system administrators mount /home as nosuid, meaning the suid bit will never be honored. Why does a system administrator want to do that? Well, you noted the first script in the article that made a suid shell? That wouldn't have worked if /tmp was mounted nosuid. Many system administrators mount only those file systems as suid which must contain suid programs, mounting all others nosuid; for instance, /home doesn't need suid programs for the system to run, so mounting nosuid is a pre-cautious action.

But there is another way. It's a bit like shotgun programming, but you can let sendmail and procmail do the dirty work for you. How does it work? Basically you let the daemon send you mail that will instruct you to perform a certain task. The receiving sendmail will look in your ~/.forward and see that you want your mail to be processed by procmail, which will perform the update script for you. Let me first give you the CGI script:

cat <<EOF
Content-type: text/plain
You are number $ACCESS to visit this page!
echo "Subject: 1928397071" | sendmail foo

First some remarks about the mail:

  • This mail has no body, which is allowed by RFC 822 (the Internet standard describing the format of mail messages). If you want to include a body, you should precede the body by a mandatory empty line.

  • In the subject we have a script part that will hint at what script to run. It has some sort of a magic cookie (1928397071) to prevent you from processing e-mail which is accidently triggered. If you want this to be a really secure cookie, you shouldn't be writing this line as a script.

Next it is time for the ~/.forward:

"|IFS=' '&&exec /usr/bin/procmail -f-||exit 75 #bar"

The program procmail needs to look in your file ~/.procmailrc (which shouldn't be world readable, if you want to use cookies for security):

:0 b
* ^From www
* ^Subject: 1928397071

So if the daemon sends a mail with a subject 1928397071, procmail will run for you:

rm /home/foo/www/access
echo $ACCESS > /home/foo/www/access

Mission accomplished: we have written something safely into a file. Are there any catches? Yeah, sure.

First of all, you need to assure that when two people try to access your page, they won't screw up your files. You need some sort of file locking mechanism (check out flock, which also is available in Perl).

Secondly, the sendmail in the CGI script is nonblocking: it doesn't wait until the update is actually done. In most cases, this doesn't really matter, as you are only doing a simple update. Otherwise, you can always try to implement the Perl client-server model.

Don't go for the easy insecure option; keep it safe. Your system administrator will appreciate it.

Hans de Vreught ( is a computer science researcher at Delft University of Technology. He has been using Unix since 1982 (Linux since 0.99.13) and is a profound MS hater (all their products are Bad Things). He likes non-virtual Belgian beer, and he is a real globe-trotter (already twice round the world).