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

I'm a bit confused by a taint warning I'm getting. After several hours simplifying my problem, I'm left with this code:
#!/usr/bin/perl -wT # use strict; my $foo = { 'clean' => 'filename.txt', 'dirty' => scalar(<>), }; open(F,"+>{$foo->{clean}}"); close(F);
which returns this: Insecure dependency in open while running with -T switch at ./taint_puzzle.pl line 9, &lt;&gt; line 1.

Now, to me this seems cut and dry - $foo->{dirty} is tainted, but $foo->{clean} smells like daisies. If the two variables are kept in seperate scalars, there's no problem - $foo->{clean} seems to be becoming tainted merely by association.

Why is it so?

- Boldra

Replies are listed 'Best First'.
Re: tainted entangled hashrefs
by MZSanford (Curate) on Aug 22, 2001 at 20:15 UTC
    This is because the taintedness is held at the var level. So, clean and dirty are keys of the same variable. So, once one of them is dirty, the whole variable %foo is considered tainted. You are correct, it is guilt by assosiation, but it is there because $foo{dirty} is as foo as any other hash-key as far as Perl knows ... more foo than scalar as it were. the same way as arrays (if i recall).
    can't sleep clowns will eat me
    -- MZSanford
      Thanks, your explanation does help. However it seems that in my example $foo is not tainted, (its value is "HASH(0x80f8a20)") - only the values of the hash reference are tainted (%$foo has become dirty because the value of one of the keys was bad). I think you knew this.

      Your explanation does mean I can push the tainted data down a level to clean up my hash-ref, eg:
      $dirty{text} = <>; $foo = { 'clean' => '/home/boldra/filename.txt', 'dirty' => \%dirty, }; open(F,"+>{$foo->{clean}}"); close(F);
      Which leaves $foo->{clean} clean as a whistle.

      But I still don't see why I have to do this. Is it because taint checking is simply clumsy, or is there actually a risk of a user supplying data that perl will accidentally interprect as the boundry between two values in an array?

      What I'm worrying about is: Is $foo->{clean} really tainted (in my original example) or is it a false positive by the taint-checking? If it's really tainted - what sort of cleaning does it need?

      Thanks,

      - Boldra
        I tested the following with "dirty" commented and uncommented:

        #!/usr/bin/perl -wT use strict; my $foo = { clean => 'test.dat', # dirty => scalar(<>), }; open (F,"+>{$foo->{clean}}"); close(F);

        As shown here, it ran with no error. That tells me that $foo->{clean} really is taint clean.

        yours,
        Michael

        from my understanding, this is mostly a problem with Taint-checking being clumsy. using <> does not give the input line the ability to modify %foo (unless you eval'd it and then it would taint anything it touches), so the keys other than dirty should stay clean, but they do not. I believe this is an implimentation detail (or bug?) in taint checking that makes all keys in a hash share the same taint-state. Maybe someone with a bit more source knowlege could confirm that ? I will start check source myself, and will post an update if i find anything.

        Update : Now i am getting too far into this (Trees? What trees ? i am in a forest !), and have found that normal hashes do not suffer this. So, in Perl 5.6.0 i used :
        %ENV = (); %foo = ( clean => '/usr/matt/.netrc', dirty => (<>), ); print "$foo{clean}\n"; sleep 1; print "$foo{dirty}\n"; sleep 2; system("ls $foo{clean}"); # ok system("ls $foo{dirty}"); # error
        but failed on a hash ref. ... still looking.


        Update Again : Ok, here is an odd one, here is my test code :
        $| = 1; %ENV = (); %foo = ( clean => '/usr/matt/.netrc', dirty => (<>), ); $bar = { clean => '/usr/matt/.profile', dirty => (<>), }; $and = \%foo; print "Foo\n"; print "1 : " ; system("echo $foo{clean} > /dev/null 2>&1"); print "2 : " ; system("echo $foo{dirty} > /dev/null 2>&1"); print "Bar\n"; print "1 : " ; system("echo \"$bar\" > /dev/null 2>&1"); print "2 : " ; system("echo $bar->{clean} > /dev/null 2>&1"); print "3 : " ; system("echo $bar->{dirty} > /dev/null 2>&1"); print "And\n"; print "1 : " ; system("echo \"$and\" > /dev/null 2>&1"); print "2 : " ; system("echo $and->{clean} > /dev/null 2>&1"); print "3 : " ; system("echo $and->{dirty} > /dev/null 2>&1");
        And the output is :
        %shell > perl -Du -U -T taintme.pl EXECUTING... a ^D b ^D Foo 1 : system 0 30004 30004 2 : system 1 30004 30004 a Bar 1 : system 0 30004 30004 2 : system 1 30004 30004 3 : system 1 30004 30004 And 1 : system 0 30004 30004 2 : system 0 30004 30004 3 : system 1 30004 30004 a
        I am now seeing this is something with the anonhash specifically, and assiging a hash-ref to an extsing hash will allow key access. Unsure why.

        Update 3: anymore updates and i will create a new node. I added some code to my last test script and got yet another oddity. I added :
        $another = +{}; $another->{clean} = '/usr/matt/.kshrc'; $another->{dirty} = (<>); # and also print "1 : " ; system("echo \"$another\" > /dev/null 2>&1"); print "2 : " ; system("echo $another->{clean} > /dev/null 2>&1"); print "3 : " ; system("echo $another->{dirty} > /dev/null 2>&1");
        and to my suprise i got :
        Another 1 : system 0 30004 30004 2 : system 0 30004 30004 3 : system 1 30004 30004
        ... so, now i think it is a bug in how tainting is assigned during the assigment. Maybe it taints everything in the {} block as a group ? thats about all i have at this point. hmmm... looking at perl -Dut -U -T taintme.pl i see that the code is parsed, and not until the end of the block it is decalred a anonymous hash. So, because it could be a code ref (maybe? i'm guessing) the taint covers the whole block. My fix is to assign dirty element seperatly line $another above.

        Update (last) : Per CB request , here is the fix fo the code :
        Taint bad ...
        #!/usr/bin/perl -wT # use strict; my $foo = { 'clean' => 'filename.txt', 'dirty' => scalar(<>), }; open(F,"+>{$foo->{clean}}"); close(F);

        ... and taint ok ...
        #!/usr/bin/perl -wT # use strict; my $foo{clean} = 'filename.txt'; $foo{dirty} = scalar(<>); open(F,"+>{$foo->{clean}}"); close(F);

        can't sleep clowns will eat me
        -- MZSanford
Re: tainted entangled hashrefs
by chipmunk (Parson) on Aug 22, 2001 at 22:26 UTC
    perlsec:
    Any variable set to a value derived from tainted data will itself be tainted, even if it is logically impossible for the tainted data to alter the variable.
    This is the case for the assignment to $foo. Because the data from scalar(<>) is tainted, the rest of the data in the construction of the anonymous hash is also tainted.

    If you want to make sure that this doesn't happen, try assigning the values to $foo separately. For example:

    $foo = { clean => 'filename.txt' }; $foo->{dirty} = <>;
Re: tainted entangled hashrefs
by Zaxo (Archbishop) on Aug 22, 2001 at 20:16 UTC

    The code's filename.txt is a relative path depending on $ENV{'PWD'}. That's tainted. Using an absolute path will settle the problem.

    Update: MZSanford is quite right, I missed that angle. Boldra's reply is mistaken that $ENV{'PATH'} enters into resolving relative paths to files. That is, as I say, done with $ENV{'PWD'}.

    After Compline,
    Zaxo

      Zaxo misspeaks... :)
      My empirical studies find that "scalar(<>)" remained untainted so long as <> was empty. Providing a file name tainted it, as did providing standard input. Reading from /dev/null did not provoke the error.
      I found no difference between relative and absolute file names for the open; it all appears to depend on <>.

      yours,
      Michael
      Sadly, that doesn't solve the problem. The warning generated by a bad use of the path is actually Insecure $ENV{PATH}.

      - Boldra
Re: tainted entangled hashrefs
by Rex(Wrecks) (Curate) on Aug 23, 2001 at 01:50 UTC
    FYI -- Network Programming with Perl by Lincoln Stein (see the Reviews Section) has a great section on using Taint and the pitfalls thereof.

    "Tainted data cannot be used in any function that affects the outside world, or Perl will die with an error message. Such functions include:
    <snip>
  • Opening a file for writing"
  • etc...

    He also says "every variable that such tainted data touches becomes tainted as well."

    Being in the same hash does mean guilty by association.

    "Nothing is sure but death and taxes" I say combine the two and its death to all taxes!