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:
||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.|
||These add the capability to repeat, concatenate, and join, as well as create anonymous data structures. They're not part of the
||These ops allow the script to use loops. They can be used to chew up all of the CPU time on a system.|
||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.
|: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
%_, along with the filehandles
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:
||Math ops that can generate a floating point exception, like
||This group includes the
||The large set of
|:browse||A handy tag name for a reasonable set of ops beyond the :default optag. Includes the :default, :sys_db, and :filesys_read tags.|
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.
||These ops include the
|: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 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
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
SafeCGIPerl consists primarily of two components:
||The suid-root executable that performs most of the ground work and then executes
|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.
safeperlparses 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 calling
getpwnam()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
safeperlexecutable sets up an environment using the UNIX
limit()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
cgiperlwith the desired scriptname expected now to be in the current working directory. The script is then executed by
Safecompartment. 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 the
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.