The Opcode
module released with Perl5.003 has a bug that can allow the script to access the shell through the Glob
operation. It's extremely important that you apply the security patch to the Opcode
module if you're using a version older than Version 1.02. Or obtain the latest version from Tim Bunce's CPAN directory.
In general, within the Opcode
module, each operation you want to mask can be specified by its canonical name, as specified in Opcode.h from the Perl source. Most of the methods from the Opcode
module take a list of operators as parameters. The list's elements can consist of an individual opname, which specifies an individual operator like open
, or an optag, preceded by a colon :
, which refers to a group of operators. The Opcode
module specifies several optags, for convenience and correctness, that can be used to allow or mask out groups of operations. The methods will also accept negated opnames or optags, prepended with a !
, which will remove the specified opname or optag from the list of ops masked up to that point. The Safe
module sets up a compartment by default that allows the operations corresponding to the optag :default
, which consists (as of this writing) of the following optags:
:base_core |
The core operations required for Perl to start up, compile a script, and run. This set lets you do the very basic things with a script, like assignments, addition, subtraction, multiplication, keys, values, split, and others. |
:base_mem |
These add the capability to repeat, concatenate, and join, as well as create anonymous data structures. They're not part of the :base_core opset because they can be used to implement a resource attack on a machine, possibly consuming all of its memory. |
:base_loop |
These ops allow the script to use loops. They can be used to chew up all of the CPU time on a system. |
:base_io |
These ops enable filehandle input. They are considered safe only when the filehandle exists before the compartment is created and is shared into the compartment. STDIN and STDOUT are shared into every Safe compartment by default, but that's it. Ops include read , readdir , print , seek , and others. |
:base_orig | These ops are still under consideration but are included in the safe compartment at this time. Ops like sprintf, crypt, select, getppid, gmtime, and others are among the hodge-podge included under this optag. This group is especially subject to change. |
Note that the Opcode
module instructs you not to rely on the definition of the :default
tag, or any other tag, for that matter. Always check any new release to be sure that the operations you expect to be included or excluded truly are or aren't.
All other operations are disallowed within the default Safe
compartment. The only variables that are shared into the default Safe
compartment include $_
, @_
, and %_
, along with the filehandles STDIN
and STDOUT
. They are shared into the default compartment upon invocation of the new()
method for Safe
, along with all the symbols that exist in the main::
namespace at the time the compartment was created. If you wish to enhance the capabilities of a given Safe
compartment, you can do so with the methods provided within the Safe
module. The other optags defined in the Opcode
module include the following:
:base_math |
Math ops that can generate a floating point exception, like atan2 , sin , cos , exp , log , and sqrt . If you allow this group, you should also set up a $SIG{FPE} handler. |
:filesys_read |
This group includes the stat , lstat , and readlink operators. In general, access to information about the filesystem, even just the information from stat() , can provide insight on potential holes in security and should be allowed with caution. |
:sys_db |
The large set of get* calls to do lookups into the system database files, like passwd or hosts. gethostbynam() , getpwuid() , and so on, belong to this group. Access to the system databases is generally a no-no but may be required to do anything truly useful. Use with caution. |
:browse | A handy tag name for a reasonable set of ops beyond the :default optag. Includes the :default, :sys_db, and :filesys_read tags. |
Caution:
The rest of this list of optags can be considered to be extremely dangerous to allow within a truly Safe
compartment. Don't allow these ops in your scripts to be executed within the Safe
compartment without understanding exactly what you're doing and whom you're doing it to. Caveat scriptor.
:filesys_open |
These ops include the open() , close() , sysopen() , opendir() , and other operations, which allow direct access to files in the system with a Perl program. |
:filesys_write |
Includes link , unlink , symlink , truncate , mkdir , rmdir , chmod , and chown operations, among others. |
:subprocess |
Backticks, system , fork , wait , and glob are included within this group. Possibly the most dangerous opcodes from a security standpoint. |
:ownprocess |
exec , kill , exit . |
:others |
msgctl and other SysV shared memory routines. |
:still_to_be_decided | chdir, flock, ioctl, socket, getpeername, bind, and the rest of the Berkeley networking calls, as well as sleep, alarm, sort, pack, and caller. This optag is quite unstable and should not be relied upon. There are ops here that may be retagged or removed in the future for various reasons. |
Any of these optags, or any of the other opnames that aren't defined in the default Safe
compartment, can be permitted via the permit()
method. After you've set up the compartment the way you wish, user code can be executed within it via the reval()
method, or a script can be compiled and executed via the rdo()
method. You should become fully acquainted with the Safe
module, in its intents and workings, before using it. This also goes for the Opcode
module. Their documentation is embedded into the modules themselves as POD. SafeCGIPerl
Now that you understand how the Safe
module works, you might think that you could create a program that would set up a Safe
compartment and then execute arbitrary Perl code for a given user. After all, if only the :default
opset is allowed, what can go wrong? Right? Well, theoretically, yes, but in practice, the :default
optag is just too restrictive to do anything useful within a given CGI script. So just how do you set up a scheme whereby the arbitrary script can be executed as a CGI? One way is to use SafeCGIPerl
. SafeCGIPerl
is a package that utilizes the Safe
module specifically to provide a means to run CGI scripts under a given user's UID, and provide a restricted environment while giving the user's code some additional leeway to perform useful tasks like sending mail. In order to use SafeCGIPerl
, you must first obtain the package from a CPAN site (available in Malcolm Beatties' CPAN directory), and then, assuming you're running Perl5.003 or later, apply the patch to use later versions of the Safe
module. Next, build the safeperl
executable and install it as a root suid program into your cgi-bin directory and install the cgiperl script into /usr/local/bin. When you're finished, your users will be able to create a $HOME/cgi/bin directory in their home directory and place scripts there that will be executed by safeperl
when the URL of the form
http://Your.Site/cgi-bin/safeperl/username/scriptname
is received by httpd. The SafeCGIPerl
scheme is quite comprehensive but also provides methods for performing rudimentary tasks that aren't available within the standard Safe
compartment. SafeCGIPerl
consists primarily of two components:
safeperl |
The suid-root executable that performs most of the ground work and then executes cgiperl . |
cgiperl | A Perl script that sets up a Safe compartment, along with some additional functionality, and then executes another (arbitrary) script running under a specified user id using the rdo() method from the Safe module. |
Let's take a look at how SafeCGIPerl works.
- First,
safeperl
parses the URL to be sure that the username is valid. It does this by first escaping any unwanted characters from the string, then parsing out the username and callinggetpwnam()
with the string it gets. If the username isn't valid, it exits with an error. If the username is valid, it logs the request and changes to the UID specified and to that user's ~/cgi/bin directory. - Once the UID and directory have been set, the
safeperl
executable sets up an environment using the UNIXlimit()
system call, which explicitly restricts some crucial system-level parameters for the process (Perl) which it's about to exec. The parameters that are restricted are CPU time, niceness, coresize, data-segment size, and resident-memory size. Doing this implicitly avoids many of the risks that can be introduced by allowing Perl ops like looping or memory-growing by specifying in advance the maximum allowable amount for these parameters. - Finally, the
safeperl
binary execscgiperl
with the desired scriptname expected now to be in the current working directory. The script is then executed bycgiperl
within aSafe
compartment. This implies, of course, that the script can't do just any old thing, but it does give the user some additional capabilities, including a subroutine for sending mail and capability to read and write files within a particular directory, along with the standard operators that are part of theSafe
:default
optag.
In general, SafeCGIPerl
is a vastly preferable alternative to allowing users to run arbitrary scripts within their own Web space via the .cgi extension. It is not, however, a foolproof solution. As always, read the documentation carefully and provide the same documentation, along with any other warnings appropriate, to your users before telling them to utilize this tool. CGIWrap
While not specifically a Perl package, CGIWrap
is another tool you can implement on a site-wide basis to allow your users the freedom to create and execute their own CGI programs. It does not, however, provide the opcode masking that SafeCGIPerl
does, nor any kind of Safe
compartment. All it does, basically, is ensure that a given user's CGI programs are run under his/her own UID. The danger to the user is still quite prominent, unless he/she is very savvy about what to do, and what not to do, with CGI programs in general. Further, if an unsavvy user allows a Trojan horse to be placed in his/her path, it can endanger the security of the entire system.