How to securely run programs from inside your program on Unix
Every so often, people feel that their program needs to be able to run
another program under some circumstances; for instance, you're writing
a mailer and you want to be able to run an external virus scanner to
see if a newly received message has a virus. A certain amount of these
people decide to use
popen() for this; as the saying
goes, now they have two problems.
(The only time it's ever okay to use these routines is if the entire command line to run is set statically in your configuration file, with no runtime expansion, substitution, or insertion of anything at all.)
Hopefully everyone understands why: both of these routines pass your
command line to the shell to have it interpret the line (that's why they
can take a 'command line' as an argument, instead of an array of command
arguments). This means that any shell metacharacters that appear in
substitutions or expansions will be interpreted by the shell, generally
with disastrous results. So the first rule of securely running other
programs is that you must execute programs yourself, using an interface
(such as the
exec*() family) that lets you directly control what the
command line arguments are.
This means that you're responsible for breaking up your 'command line' into command line arguments, and as it happens there's a right and a wrong way to do this. The right way is to tokenize your command line before you perform substitutions.
Suppose that you have a hypothetical command line:
/opt/scanner $spoolfile $sender $recipient
If you're directly running
/opt/scanner, this looks safe; it will get
called with three command line arguments and will be happy. But suppose
that someone manages to sneak a space into
$sender. If you expand
the variables in this command line before breaking it up into arguments,
the scanner is suddenly getting called with four arguments, which is not
good. (And even without that it could be called with two arguments if
$sender is sometimes allowed to be empty.)
The proper way to do this is to break this template command line up into
arguments, then expand each argument separately. No extra arguments will
be created no matter what spaces or other characters are introduced by
the expansion, and if an expansion is empty you won't delete arguments
(the scanner will instead see an empty
$sender argument). This is
exactly what you want to happen.
(My off the cuff instinct is that tokenization before expansion is in fact what you want for general string substitutions, but I haven't thought deeply about the issue. It certainly is what you want in this specific situation.)
Some people will say that they really do need the shell because they need to do things like redirection and substituting shell variables. The right answer is for these people to write a little shell script around their real program, not for your program to use the shell to run everything. A similar answer applies to people who need string substitution to retokenize. In both cases the principle is that if you have security you can always give it away, but if you don't have security to start with you cannot get it back.
(If people often need redirection or shell variables or retokenization after substitution, you have a deeper design issue. Find out why they keep needing to do these things in your system and fix it so that they don't.)