Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

# This subroutine returns true if its argument is tainted, false otherwise.
sub tainted { local($@); eval { kill 0 * $_(0) }; $@ =~ /^Insecure/; } 1;
I found the above code on the internet. It claims to do taint checking, but I'm not savvy enough to understand whats going on. I don't want to trust this subroutine to verify that my form data is safe if I don't know understand it.

As I understand the first bit, it somehow evaluates $_(0) to see if any processes start (and kills them). I think...and why does it only have to evaluate the first element of the array?

And I'm not sure under what circumstance $@ would match ^Insecure.
Thanks.

Edit: BazB added code tags.

Edit: ysth change title from: explanation please?

Replies are listed 'Best First'.
Re: Please explain this tainting behaviour
by Aristotle (Chancellor) on Aug 05, 2004 at 16:31 UTC

    The key is that it uses $_[0] as part of the argument to the kill function. That function is affected by taint checks; if $_[0] is tainted, Perl will refuse to let it or something calculated from get passed to kill. It will raise an exception instead.

    Normally, an exception aborts your program. However, in this case, the statement in question is inside an eval BLOCK construct. If an exception occurs inside, only the code in that block is aborted, and the surrounding code can examine the exception by looking at the $@ variable. The above code does that to check whether it's a Perl error message starting with "Insecure", which would happen if $_[0] was tainted.

    Multiplying $_[0] with zero is a trick. If $_[0] is not tainted, kill will be called. Despite the name, kill does not necessarily kill processes; it sends them signals. By multiplying with zero, you make sure that you always send signal zero, and that is a no-op that does nothing to the receiving process. Sending signal zero is normally used to check if a process with the given PID exists.

    Makeshifts last the longest.

Re: Please explain this tainting behaviour
by ccn (Vicar) on Aug 05, 2004 at 16:40 UTC

    As for me, Scalar::Util has more natural way to detect tainted variables

    perldoc Scalar::Util|grep taint use Scalar::Util qw(blessed dualvar isweak readonly refaddr re +ftype tainted weaken isvstring looks_like_number set_prototype); tainted EXPR Return true if the result of EXPR is tainted $taint = tainted("constant"); # false $taint = tainted($ENV{PWD}); # true if running unde +r -T
Re: Please explain this tainting behaviour
by hardburn (Abbot) on Aug 05, 2004 at 16:32 UTC

    If code inside an eval block dies, that error message is captured and put inside $@. The message for doing something bad with tainted data is something like "Insecure dependency in . . . ". So if the data is tainted, $@ will match /^Insecure/.

    The match actually doesn't need to run. The eval will return false if it dies, so all that's really needed is to check the return value from eval. This is what Test::Taint does.

    "There is no shame in being self-taught, only in not trying to learn in the first place." -- Atrus, Myst: The Book of D'ni.

Re: Please explain this tainting behaviour
by davido (Cardinal) on Aug 05, 2004 at 16:36 UTC

    You do have a syntactical error in the script. One of the lines should read as follows:

    eval { kill 0 * $_[0] };

    Note the use of square brackets instead of parenthesis.

    Also, you asked about why only $_[0] is evaluated. The sub tainted() is apparently intended to take a single scalar as its argument. That scalar is held (loosely speaking) as the first element of the special variable @_. You can read up on that in perlvar and perlsub.


    Dave

      Thanks for the replies everyone, makes sense. The bracket problem I meant to mention - when I previewed the post the brackets weren't coming out, so i put parentheses in.

      To get some more clarification - do I need to be running -T for this to work? I was hoping I could just pass all my parameters through it and get a "this data is OK" or "not OK".

        Yes, you'll have to use -T to enable taint checking. It cannot do your work for you, though. It is merely prevents you from accidentally using unfiltered user input to perform dangerous operations. The onus for defining what data is well-formed and safe to accept and what's not, though, is still on you.

        The only way to get untainted data from a tainted variable is to perform a pattern match, and capture some or all of the data. The captured data is then untainted. F.ex, if you have an input value that must only consist of digits, you could untaint it like so:

        unless( $some_user_input =~ /^(\d+)$/ ) { die "You did not pass only digits for some_input\n"; # or you produce an error page here or send the user back if it's +a CGI, f.ex } my $untainted_user_input = $1;

        Now you can perform dangerous operations using $untainted_user_input.

        Of course, nothing stops you from using /(.*)/s as the test pattern, therefor accepting any input at all and thus defeating the point of taint checks.

        Ovid's excellent CGI course has an enlightening chapter on how to untaint data sensibly, treating taint checking as an ally that will help you avoid getting exploited.

        There are modules on CPAN that will help you with common untainting tasks — look for the various Untaint modules.

        Makeshifts last the longest.

Re: Please explain this tainting behaviour
by roju (Friar) on Aug 05, 2004 at 16:32 UTC
    perldoc -f kill claims that kill with a signal 0 does nothing. 0 * anything is 0, so if $_[0] is not tainted, nothing happens.

    However, if $_[0] is tainted, then the code will die with "Insecure dependency in kill while running with -T switch at -e line 1, <STDIN> l ine 1.". So if $_[0] is tainted, $@ matches. If $_[0] is not tainted, no match.