Many scripts need to run with user permissions different from those of the web server itself. On a Unix computer, the easiest way to do this is to make the script SUID or SGID. By doing this, the script runs with the permissions of the owner of the file, rather than those of the web server itself. On Macintosh, DOS, and Windows 95 systems, there is no such choice—scripts run with the same privileges and can access everything on the system.
Unfortunately, programs that run with additional privileges traditionally have been a source of security problems. This list of suggestions is based on those problems and specially tailored for the problems faced by the web developer:
Avoid using the superuser (SUID root or SGID wheel) unless it is vital that your program perform actions that can only be performed by the superuser. For example, you will need to use SUID root if you want your program to modify system databases such as /etc/passwd. But if you merely wish to have the program access a restricted database of your own creation, create a special Unix user for that application and have your scripts SUID to that user.
If your program needs to perform some functions as superuser but generally does not require SUID permissions, consider putting the SUID part in a different program and constructing a carefully controlled and monitored interface between the two.
If you need SUID or SGID permissions, use them for their intended purpose as early in the program as possible and then revoke them by returning the effective and real UIDs and GIDs to those of the process that invoked the program.
Avoid writing SUID scripts in shell languages, especially in csh or its derivatives.
Consider creating a different username or group for each application to prevent unexpected interactions and amplification of abuse.
In general, use the setuid( ) and setgid( ) functions to bracket the sections of your code that require superuser privileges. For example:
setuid(0); /* Become superuser to open the master file */ fd = open("/etc/masterfile",O_RDONLY); setuid(-1); /* Give up superuser for now */ if(fd<0) error_open( ); /* Handle errors */
Use the full pathnames for all files that you open.
For scripts, use the chroot( ) call for further restricting your script to a particular directory. The chroot( ) call changes the root directory of a process to a specified subdirectory within your filesystem. This change essentially gives the calling process a private world from which it cannot escape. For example, if you have a program that only needs to listen to the network and write into a log file that is stored in the directory /usr/local/logs, then you could execute the following system call to restrict the program to that directory:
chroot("/usr/local/logs");
Use the chroot( ) call only with CGI programs, never with modules that are called by an API. Because of the difficulties with shared libraries on some systems, you may find it easier to use chroot( ) with Perl than with C.