Beefy Boxes and Bandwidth Generously Provided by pair Networks
go ahead... be a heretic
 
PerlMonks  

Insecure Dependency in Taint Mode

by Bod (Parson)
on Nov 04, 2022 at 22:33 UTC ( [id://11147972]=perlquestion: print w/replies, xml ) Need Help??

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

I am putting a unique string of text onto a PDF document using PDF::API2. Here is the minimal code to demonstrate:

#!/usr/bin/perl -T use CGI::Carp qw(fatalsToBrowser); use FindBin qw($RealBin); my $safepath; BEGIN { if ($RealBin =~ m!^(/home/...path.to.site.../(test|uk)/www)!) { $safepath = "$1/../lib"; } else { die "Insecure access!"; } } use lib "$safepath"; use cPanelUserConfig; use PDF::API2; use strict; use warnings; my $pdf = PDF::API2->open("$ENV{'DOCUMENT_ROOT'}/../data/xmas/GiftSub +scription.pdf"); my $font = $pdf->font("$ENV{'DOCUMENT_ROOT'}/../data/xmas/Merriweather +.ttf"); my $page = $pdf->open_page(1); my $text = $page->text; $text->font($font, 36); $text->position(656, 403); $text->text('ABC-123'); $pdf->save("$ENV{'DOCUMENT_ROOT'}/test.pdf"); print "Content-type: text/plain\n\n"; print "$ENV{'HTTP_HOST'}/test.pdf\n";
This works as expected when taint mode is off. But, once taint mode is turned on I get:
Insecure dependency in open while running with -T switch at /usr/lib64 +/perl5/IO/File.pm line 187

I have replaced $ENV{'DOCUMENT_ROOT'} with hardcoded paths to ensure that it is not this that is causing the problem.

Is it possible to use PDF::API2 in taint mode or do I have to choose between finding another module or turning off taint mode?

Replies are listed 'Best First'.
Re: Insecure Dependency in Taint Mode
by kcott (Archbishop) on Nov 05, 2022 at 01:15 UTC

    G'day Bod,

    "Is it possible to use PDF::API2 in taint mode ..."

    I'd say the short answer is "yes".

    Obviously, I can't reproduce your environment — apart from anything else, there's insufficient information, such as your Apache? configuration — however, the following test works fine.

    ken@titan ~/tmp/www $ perl -Mstrict -Mwarnings -Mautodie=:all -MCarp::Always -T -E ' use FindBin qw{$RealBin}; my $safepath; BEGIN { if ($RealBin =~ m!^(/home/.+?/(tmp|xyz)/www)!) { $safepath = "$1/../lib"; } else { die "Insecure access!"; } } use lib "$safepath"; use PDF::API2; my $pdf = PDF::API2->new("some.pdf"); $pdf->save("some.pdf"); '

    Changing the last two (new() & save()) lines to:

    my $pdf = PDF::API2->open("some.pdf");

    also works without any problems. I checked:

    ken@titan ~/tmp/www $ file some.pdf some.pdf: PDF document, version 1.4, 0 pages

    I'm running Perl v5.36.0 and PDF::API2 v2.043.

    You've included completely unknown code with "use cPanelUserConfig;". Clearly, we're unable to test that. Try removing all of the code related to PDF::API2 and see if cPanelUserConfig is the cause of your taint issue. Your report suggests that you think PDF::API2 is the source of the problem, but you don't say why; you could be working from a false premise.

    I'd move strict and warnings to the start of your code. Was there a reason for putting these in the middle of the script?

    It's been 10-20 years since I last wrote any CGI code; my knowledge is certainly not current. I seem to recall that its documentation included troubleshooting tips involving running a CGI script from the command line. Perhaps look into that and see if it's any help.

    Have a look at "FindBin: KNOWN ISSUES" and what it says about issues with mod_perl. I'm very much guessing with this — I don't even know if you're using mod_perl — but it may be worth a look.

    Take a look through "perlsec - Perl security" for anything that might help. Perhaps the suggested:

    delete @ENV{qw(IFS CDPATH ENV BASH_ENV)}; # Make %ENV safer

    If none of those suggestions help, you'll have to step through your code to find out where problems are occurring.

    — Ken

      I'd say the short answer is "yes"

      That's encouraging - thanks kcott

      You've included completely unknown code with "use cPanelUserConfig;"

      cPanelUserConfig is a cPanel module. It performs some magic with @INC so that user installed modules can be found by Perl. PDF::API2 is a module I have installed so without cPanelUserConfig we won't get very far. Pretty much every script I write server side has this so I can be pretty sure this is not the cause of the issue.

      I'd move strict and warnings to the start of your code. Was there a reason for putting these in the middle of the script?

      As both strict and warnings operate for all the block regardless of where they are declared, I tend to use them to separate the head code from the actual mechanics of code. I'm sure there are proper terms that I don't know but by "head code" I mean the part of the code that sets thing up correctly such as the shebang, setting paths and bringing in modules to use. Everything below is what actually does the work of the script.

      Your report suggests that you think PDF::API2 is the source of the problem, but you don't say why

      Sorry - I should have explained that identical head code exists in all the other scripts on the site and they all run with taint mode on. It's only since I tried using PDF::API2 that I have had this problem and the error reports it as being in File which is used by PDF::API2 and not called by my script anywhere else.

      I'm out of the office currently but will look at the other things you suggest a little later and try removing/substituting code until I find the exact cause of the issue.

        "cPanelUserConfig ..."

        I don't really want to harp on cPanelUserConfig. Here's a few quick notes:

        • I wasn't suggesting cPanelUserConfig was a problem; it was an unknown, so therefore a possible culprit.
        • I followed your "cPanel" link. It seems that I'd need to create an account to learn more about cPanelUserConfig (or search the web for an unofficial copy). I wasn't prepared to do either, so it remains an "unknown" for me.
        • cPanelUserConfig may be a handy shortcut, which is fine; however, your use of the lib pragma shows that you already know how to manipulate @INC, so it's not quite as absolutely essential as you may think.
        • I'd aim to move from "... pretty sure this is not the cause ..." to "... 100% certain this is not the cause ...".
        "As both strict and warnings operate for all the block regardless of where they are declared, ..."

        No, that's incorrect. Here's a handful of examples that I threw together to demonstrate:

        $ perl -e 'BEGIN { $x = 1 } use strict;' $ perl -e 'use strict; BEGIN { $x = 1 }' Global symbol "$x" requires explicit package name (did you forget to d +eclare "my $x"?) at -e line 1. BEGIN not safe after errors--compilation aborted at -e line 1. $ perl -e 'local $x; use strict;' $ perl -e 'use strict; local $x;' Global symbol "$x" requires explicit package name (did you forget to d +eclare "my $x"?) at -e line 1. Execution of -e aborted due to compilation errors. $ perl -e 'use strict; local $x; no strict "vars";' Global symbol "$x" requires explicit package name (did you forget to d +eclare "my $x"?) at -e line 1. BEGIN not safe after errors--compilation aborted at -e line 1. $ perl -e 'use strict; no strict "vars"; local $x;' $
        "... the error reports it as being in File ..."

        Actually, the error reports ".../IO/File.pm" which is the core module IO::File. I seem to recall that you were stuck with a fairly early version of Perl by your ISP; although, I do think IO::File would have been available in whatever early version that was:

        $ corelist IO::File Data for 2022-05-27 IO::File was first released with perl 5.00307
        "... which is used by PDF::API2 and not called by my script anywhere else."

        You don't necessarily need to have a "use IO::File;" statement. Consider these two:

        $ perl -E 'print("Hello, world!\n"); say $INC{"IO/File.pm"};' Hello, world! $ perl -E 'STDOUT->print("Hello, world!\n"); say $INC{"IO/File.pm"};' Hello, world! /home/ken/perl5/perlbrew/perls/perl-5.36.0/lib/5.36.0/cygwin-thread-mu +lti/IO/File.pm
        "... will look at the other things you suggest a little later ..."

        You might also like to try Carp::Always to get verbose backtraces. It can produce a lot of output, but could be a quick way to track down the source of the "Insecure dependency ..." message.

        Good luck with your troubleshooting. :-)

        — Ken

        ... both strict and warnings operate for all the block regardless of where they are declared ...

        My understanding is that both strict and warnings are lexical.

        c:\@Work\Perl\monks>perl $x = 42; print "$x \n"; print ">$y< \n"; use strict; use warnings; ^Z 42 ><


        Give a man a fish:  <%-{-{-{-<

        "As both strict and warnings operate for all the block regardless of where they are declared, I tend to use them to separate the head code from the actual mechanics of code."

        This is not true, and you can verify it using:

        $x = 1; use strict; $y = 2;

        Perl will complain about the use of symbol $y, but not $x because $x was used before strictures.

        Both strict and warnings use lexical scope, which means their effect starts where they're imported, and continues until the end of the block they were imported into (that is, the closing curly brace "}") or if they weren't imported into a block, until the end of the file.

Re: Insecure Dependency in Taint Mode
by pryrt (Abbot) on Nov 05, 2022 at 19:19 UTC
    Bod,

    The problem you are having with taint here is exactly the same problem that you had before you learned how to untaint RealBin. Any time to want to use an external variable, like the $FindBin::RealBin or an $ENV{...} environment variable to access the filesystem, you have to untaint it.

    I used the command line on my cPanel-based webhost to prove the point: running DOCUMENT_ROOT=/home1/pryrtcom/public_html perl -T sscce-t.pl :

    DOCUMENT ROOT = /home1/pryrtcom/public_html inside eval block to avoid dying on the tainted environment variable Outside of eval block ROOT = /home1/pryrtcom/public_html inside second eval block to avoid dying on the tainted environment var +iable eval 2 didn't die if this prints it did not die because I untainted /home1/pryrtcom/public_html.

    source:

    #!/usr/bin/perl -T use warnings; use strict; use autodie; use cPanelUserConfig; use PDF::API2; BEGIN { print "content-type: text/plain;\n\n"; $|=1; } my $pdf = PDF::API2->new(); $pdf->save('/tmp/new.pdf'); print "I can run to here in taint mode, so it's _not_ PDF::API2 that c +auses the taint issue\n"; print "DOCUMENT ROOT = $ENV{DOCUMENT_ROOT}\n"; eval { # avoid dying for the tainted variable print "inside eval block to avoid dying on the tainted environment + variable\n"; my $pdf_local = PDF::API2->open("$ENV{'DOCUMENT_ROOT'}/../local.pd +f"); print "eval 1 didn't die if this prints\n"; }; print "Outside of eval block\n"; $ENV{DOCUMENT_ROOT} =~ m/^(.*)$/; # not a safe untaint; you should do + real checking on DOCUMENT_ROOT for safety my $root = $1; print "ROOT = $root\n"; eval { # avoid dying for the tainted variable print "inside second eval block to avoid dying on the tainted envi +ronment variable\n"; my $pdf_local = PDF::API2->open("$root/../local.pdf"); print "eval 2 didn't die if this prints\n"; print "it did not die because I untainted $ENV{DOCUMENT_ROOT}.\n"; 1; } or die "eval failed: $@";

    Learn the lesson of taint: essentially anytime you use a variable that comes from the outside world, you have to untaint it before using it to access the filesystem. (Second lesson: taint error messages are unhelpful. The problem is not with IO::File, but with using a tainted variable to try to access the filesystem.)

      Thank you pryrt - very helpful

      But in the original post I wrote:

      I have replaced $ENV{'DOCUMENT_ROOT'} with hardcoded paths to ensure that it is not this that is causing the problem

      However, given your comments and the troubleshooting help from kcott, I shall investigate further after food and watching some fireworks out of the window as it is Guy Fawkes Night here in the UK (much to the annoyance of Boomer, the office hound).

        I have replaced $ENV{'DOCUMENT_ROOT'} with hardcoded paths

        Sorry I hadn't noticed that statement. Though if you knew that already, it would have been nice if your SSCCE had removed that distraction. Especially because when I ran my original code, I was able to show a taint problem with $ENV{'DOCUMENT_ROOT'} and no problem when I had an untainted variable instead. I assumed that must be the culprit, since it matched your shown code.

        Taking out that, I can run an equivalent of every single line from your SSCCE script, with some extra debug prints, without flagging a taint problem.

        Command: HTTP_HOST=127.0.0.1 perl -T sscce-t.pl

        Output:

        content-type: text/plain; RealBin = /home1/pryrtcom PDF::API2::VERSION = 2.043 ROOT = $root = /home1/pryrtcom/public_html [Sat Nov 5 16:07:36 2022] sscce-t.pl: Use of uninitialized value in v +ec at /home1/pryrtcom/perl5/lib/perl5/PDF/API2/Resource/CIDFont/TrueT +ype/FontFile.pm line 554. [Sat Nov 5 16:07:36 2022] sscce-t.pl: Use of uninitialized value in s +calar assignment at /home1/pryrtcom/perl5/lib/perl5/PDF/API2/Resource +/CIDFont/TrueType/FontFile.pm line 554. Font => PDF::API2::Resource::CIDFont::TrueType=HASH(0x469f530) Page => PDF::API2::Page=HASH(0x2c752e8) Text => PDF::API2::Content::Text=HASH(0x3b97148) set font => PDF::API2::Content::Text=HASH(0x3b97148) set pos => PDF::API2::Content::Text=HASH(0x3b97148) set text => 156.096 save => Content-type: text/plain 127.0.0.1/../output.pdf

        Source:

        (I tried with a font I uploaded, or one that was present on my host. Either one gave me the error when I ran the $pdf->font(...) line. I don't know if you're just not seeing that error because it's in a server logfile that you haven't checked, or whether you're not getting that warning, maybe because of using a different font or a different version of PDF::API2 -- that's one of the reasons I included the print of the module version in my code.)

        But with that code, I could not replicate your taint error.

        To emphasize to the advice from kcott, you need to narrow it down to which line of code is actually causing the taint problem. His example of loading a PDF and then immediately saving it (instead of loading, manipulating, and saving) will narrow it down to whether it's one of your manipulation commands that's causing the problem, or just writing the PDF to disk. Also, wrapping individual commands in eval (like I did in my first code example), with extra debug prints around, so you know exactly where it happens, would also be helpful to you. Also, if you have shell access to your host, it would be good to try running it from the command-line rather than just through the browser -- this will make it easier to see side warnings that are buried in a log file you haven't checked, and will also show if there's maybe something different going on between running through web interface and running through your host's command line.

        But as my two SSCCE's have shown, there is nothing inherently taint-unsafe with any of the PDF::API2 v2.043 commands that I ran, which I believe match in spirit the method calls you showed, so the problem seems to me to be something unique about the way you are using them, or arguments that you are passing to them, rather than inherent to the library.

Re: Insecure Dependency in Taint Mode
by afoken (Chancellor) on Nov 06, 2022 at 11:52 UTC

    (Perhaps you should re-read Re: When not to use taint mode.)

    Insecure dependency in open while running with -T switch at /usr/lib64/perl5/IO/File.pm line 187

    That's a hint, but not very helpful. So you managed to pass a tainted value to some IO::File method that calls open. In the current version of IO::FIle (v1.48), line 187 is at the end of the IO::File->open() method.

    Luckily, the Carp::Always module can help here. I use a simpler example to demonstrate it:

    #!/usr/bin/perl -T # This is taint.pl use strict; use warnings; use IO::File; my $fn=$ARGV[0] or die "Missing filename"; my $fh=IO::File->new(); $fh->open($fn,'w') or die "open $fn failed: $!"; $fh->print("This should not happen!");

    Note that you need to start perl with the -T flag if it is also in the #! line:

    /tmp>perl -T taint.pl /dev/null Insecure dependency in open while running with -T switch at /usr/lib64 +/perl5/IO/File.pm line 184. /tmp>perl -MCarp::Always -T taint.pl /dev/null Insecure dependency in open while running with -T switch at /usr/lib64 +/perl5/IO/File.pm line 184. IO::File::open(IO::File=GLOB(0x1520be8), "/dev/null", "w") cal +led at taint.pl line 10 /tmp>

    Now, that's more helpful error message. I messed up line 10 of my test script, passing a tainted $fn to IO::File->open(). 'w' can't be tainted, as it is a constant. Why is $fn tainted? Because it was copied from the tainted list of arguments @ARGV. (In theory, $fh could also be tainted, depending on what IO::File->new() does.)

    Update: How to debug CGIs from the command line: Re: Running a CGI script from a command line?

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11147972]
Approved by kcott
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others about the Monastery: (4)
As of 2024-03-29 09:31 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found