| |
Setuid Scripts
Perl is designed to make it easy to
write secure setuid and setgid scripts. Unlike shells, which are
based on multiple substitution passes on each line of the script,
perl uses a more conventional evaluation scheme with fewer
hidden "gotchas". Additionally, since the language has
more built-in functionality, it has to rely less upon external (and
possibly untrustworthy) programs to accomplish its purposes.
In an unpatched 4.2 or 4.3bsd kernel,
setuid scripts are intrinsically insecure, but this kernel
feature can be disabled. If it is, perl can emulate the
setuid and setgid mechanism when it notices the otherwise useless
setuid/gid bits on perl scripts. If the kernel feature isn't
disabled, perl will complain loudly that your setuid
script is insecure. You'll need to either disable the kernel
setuid script feature, or put a C wrapper around the script.
When perl is executing a setuid script, it
takes special precautions to prevent you from falling into any
obvious traps. (In some ways, a perl script is more secure than
the corresponding C program.) Any command line argument,
environment variable, or input is marked as "tainted",
and may not be used, directly or indirectly, in any command that
invokes a subshell, or in any command that modifies files,
directories or processes. Any variable that is set within an
expression that has previously referenced a tainted value also
becomes tainted (even if it is logically impossible for the
tainted value to influence the variable). For example:
$foo = shift; # $foo is tainted
$bar = $foo,'bar'; # $bar is also tainted
$xxx = <>; # Tainted
$path = $ENV{'PATH'}; # Tainted, but see below
$abc = 'abc'; # Not tainted
system "echo $foo"; # Insecure
system "/bin/echo", $foo; # Secure (doesn't use sh)
system "echo $bar"; # Insecure
system "echo $abc"; # Insecure until PATH set
$ENV{'PATH'} = '/bin:/usr/bin';
$ENV{'IFS'} = '' if $ENV{'IFS'} ne '';
$path = $ENV{'PATH'}; # Not tainted
system "echo $abc"; # Is secure now!
open(FOO,"$foo"); # OK
open(FOO,">$foo"); # Not OK
open(FOO,"echo $foo|"); # Not OK, but...
open(FOO,"-|") || exec 'echo', $foo; # OK
$zzz = `echo $foo`; # Insecure, zzz tainted
unlink $abc,$foo; # Insecure
umask $foo; # Insecure
exec "echo $foo"; # Insecure
exec "echo", $foo; # Secure (doesn't use sh)
exec "sh", '-c', $foo; # Considered secure, alas
The taintedness is associated with each
scalar value, so some elements of an array can be tainted, and
others not.
If you try to do something insecure, you
will get a fatal error saying something like "Insecure
dependency" or "Insecure PATH". Note that you can
still write an insecure system call or exec, but only by
explicitly doing something like the last example above. You can
also bypass the tainting mechanism by referencing subpatterns--\c
perl presumes that if you reference a substring using $1,
$2, etc, you knew what you were doing when you wrote the pattern:
$ARGV[0] =~ /^-P(\w+)$/;
$printer = $1; # Not tainted
This is fairly secure since \w+ doesn't
match shell metacharacters. Use of .+ would have been insecure,
but perl doesn't check for that, so you must be careful
with your patterns. This is the ONLY mechanism for untainting
user supplied filenames if you want to do file operations on them
(unless you make $> equal to $<).
It's also possible to get into trouble with
other operations that don't care whether they use tainted values.
Make judicious use of the file tests in dealing with any user-supplied
filenames. When possible, do opens and such after setting $> = $<. Perl
doesn't prevent you from opening tainted filenames for reading,
so be careful what you print out. The tainting mechanism is
intended to prevent stupid mistakes, not to remove the need for
thought.
|
|