http://www.developer.com/

Back to article

# Security Issues in Perl Scripts: Perl Taint Mode

February 7, 2001

How can Perl stop you from doing the mistakes that we discussed in our previous article? It would be nice if potentially risky behaviors were forbidden, but that would limit the power of Perl. It would also be nice if it weren't easy to accidentally introduce a problem. Nonetheless, Perl is unlikely to change.

Perl does has a special security mode called taint mode which can be entered by giving Perl the --T command-line option. While in taint mode, Perl carefully monitors all information that comes from outside your program and issues warnings when you attempt to do something potentially dangerous with this information. The things that taint Perl monitors include user input, environmental variables, and program arguments. Suppose we have a little script like this one:

  #!/usr/bin/perl -T $username = ; chop$username; system ("cat /usr/stats/$username"); Notice the --T option at the end of the first line. Notice also that we are using the one argument version of system(). When we execute this script, Perl enters taint mode and then tries to compile the program. The first thing that taint mode notices is that we haven't explicitly initialized our PATH variable. It issues a warning like this and aborts compilation:   Insecure$ENV{PATH} while running with -T switch at ./catform.pl line 4, chunk 1.

So we go back to our program and we modify it as discussed in section \ref{sec-insecpath} . Our program now looks somewhat like this:

  #!/usr/bin/perl -T use strict;# use this when possible $ENV{PATH} = join ':' => split (" ",<< '__EOPATH__'); /usr/bin /bin __EOPATH__ my$username = ; chop $username; system ("cat /usr/stats/$username");

Taint mode now realizes that the $username variable comes from outside our little world and may be tainted. So when you ask Perl to do something as drastic as that system() call, it gets scared and aborts the compilation with another warning:   Insecure dependency in system while running with -T switch at ./catform.pl line 9, chunk 1. It should be noted that taint mode does track the flow of your data on its way from the user to a system command. If we were to assign$username to some other variable for example, this other variable would become tainted too.

If we go ahead and split the system() call arguments in two parts, as in:

  system ("cat", "/usr/stats/$username"); taint mode is happy. But as we have previously seen, this doesn't necessarily make your script secure -- the two-argument version of system() can still be dangerous. Taint mode doesn't know about every possible vulnerability and although it can help you avoid many weaknesses in your programs, taint mode is not the ultimate solution. Here is a list of functions that Perl considers dangerous while in taint mode: exec() -- executes a system command and passes the program flow control to it; system() -- same as exec(), except it forks a child process first and waits for it to return; open() -- opens a file for input or output and associates a file handle with it; glob() -- expands a filename pattern to a full pathname according to the rules used by the shell; unlink() -- deletes one or more files; mkdir() -- creates a directory; chdir() -- changes the current directory; rmdir()-- deletes a directory; chown() -- changes the ownership information (UID and GID) of one or more files; chmod() -- changes the permissions of one or more files; umask() -- sets the umask that the process will use to mask file permissions; link() -- creates a new hardlink to a file; symlink() -- creates a new symbolic link to a file; kill() -- sends a signal to one or more processes; eval() -- parses and evaluates Perl code; truncate() -- truncates a file to a specified length; ioctl() -- manipulates device parameters of special files; fcntl() -- manipulates file descriptors; chroot() -- makes a directory the new root directory for all further pathnames that starting with '/'; setpgrp() -- sets the current process group for a specified PID; setpriority() -- sets the current priority of a process; syscall() -- performs a system call with the specified arguments; socket() -- opens a socket and attaches it to a file handle; socketpair() -- creates an anonymous pair of sockets; bind() -- binds a network address to a socket; connect() -- connects to a remote socket; All of those are functions that either access the file system somehow or are used to interact with other processes (except eval() which is too powerful to be easily secured). When given arguments which come directly from the user those functions have the potential to be misused (maliciously or accidentally) and cause harm. Taint mode also considers insecure the use of backticks and the s switch which sets the values of variables if a command-line switch of the same name is passed to the program and therefore can be maliciously used to overwrite important variables. Like that system() call in our example, some things can slip around Perl's taint mode and cause security problems if not used with care. The open() function accepts a tainted filename for read-only opens (when used with the "<" qualifier). A poor validation in a CGI script may give the distant user access to any file to which the HTTP daemon has access. Taint mode never complains about using sysopen() with user-supplied input. Thus something like:   sysopen (FH,$userinput, O_WRONLY|O_CREAT);

can be disastrous even when taint mode is on.

When using the CGI.pm module, taint mode does not consider a construct like

  \$someparameter = param ('level');

insecure, although it certainly implies a possibility of tainted user input.

How useful is taint mode? The idea behind it is one that you should try to stick to whether or not you actually use taint mode -- threat all user data as contaminated unless it has been explicitly cleaned of all impurities. It is always a good idea to turn on taint mode in security critical applications (see the Perl security man page for more information on taint mode and how to untaint data), but keep in mind that taint mode does overlook some details.

### Conclusion

There is a long established general approach in solving computer problems. In order to simplify a problem that we must solve we often make numerous assumptions that scale a complex situation down to a simpler computational model that is easier to work with. When we make those assumptions, we sometimes neglect to consider and take care of special situations, which may occur. As a result, our solution may handle well a restricted set of initial conditions, but completely fail under other less common situations. The consequences of the latter may range from incorrect results to system corruption, denial of service, and a broad spectrum of security problems.

This is very close to what often happens with Perl programs. When we read input from the user or from the environment we make all kinds of assumptions about the validity of this input, because this makes coding easier. When we copy data to a buffer, we assume that the buffer is large enough to contain this data. When we check for the existence of a file and then open it for writing, we assume that nothing alters our file between those two operations. All of these assumptions are intuitive to us, because this is how things normally proceed. But should one little thing go wrong, the results can be disastrous. This is the task of a cracker - to find out what assumptions were made when the target software was designed and to force the occurrence of uncommon conditions that the software can't handle. The task of a programmer writing a robust and secure piece of software is therefore to make as few assumptions as possible and brainstorm to find all the things that can go wrong.

Thanks for your registration, follow us on our social networks to keep up-to-date