http://qs1969.pair.com?node_id=1060460


in reply to Dangerous Characters for system calls

Thanks for all the good advice. Perhaps I should clarify what I'm doing to help narrow down the advice I need. I'm opening a filehandle that goes something like "| commanda | commandb | sendmail", and then need to feed the form input, after some header information, into this filehandle. Is this a safer or more dangerous way than doing some other thing? Is there even a need to escape characters that are being piped into a command filehandle?
  • Comment on Re: Dangerous Characters for system calls

Replies are listed 'Best First'.
Re^2: Dangerous Characters for system calls
by graff (Chancellor) on Apr 14, 2014 at 02:49 UTC
    Sorry if this reply is coming too late to be helpful. The situation you describe, of printing the text from the form directly into a pipeline of chained commands, could be relatively risk-free, provided that the commands behave reasonably well when presented with untrusted input on stdin.

    That is, if "commanda" and "commandb" really are just (fairly robust) stdin-stdout filters - and if the strings to run those commands are fully defined in your code (i.e. do not contain untrusted strings from the web form), then you won't really be exposing any untrusted data as part of a command line.

    Obviously, if "commanda" or "commandb" are not robust when given untrusted input (e.g. if they assume line-oriented input but don't know how to handle really long input lines, or they assume ASCII-only input and do unpredictable things with non-ASCII data), then your process is still facing risks, unless you filter the data appropriately before writing it to the pipe.

Re^2: Dangerous Characters for system calls
by Anonymous Monk on Mar 04, 2014 at 23:38 UTC

    Briefly: Avoid the shell! Use the form of system with multiple arguments that bypasses the shell (see also exec). Your security worries about anything [^A-Za-z0-9] will go away. Get your piping needs a different way:

    system() won't let you capture the command's STDOUT, you can get that with the LIST form of open (see also Safe Pipe Opens) or the easier-to-use capturex from IPC::System::Simple. Or, the commands you are calling may have switches that get them to write their output to a (temporary) file.

    The commands you are calling probably don't require their input on STDIN? Write your input for each command into a temporary file and pass the filename to the command via its command line.

    There's also IPC::Run3, which can avoid the shell (pass an arrayref as the first argument) and which allows you to redirect STDIN, STDOUT and STDERR. (One small downside being there's no support for piping stuff directly from one command to the next, so as above you'll have to keep things in memory or in temp files in between commands.)

    As for any other modules, carefully read their documentation to see if they allow you to bypass the shell or not, in my experience many of them don't.

    Lastly, there are usually lots of system commands that can be emulated in Perl directly (often with CPAN modules), such as sendmail, so you might not need all of those external programs in the first place.

    Yes, it's a little more work and a couple of extra temp files than just using good old shell pipes, but I've learned to love having less potential issues to worry about :-)

    Note: File::Temp's UNLINK option is useful.