Perl的污点模式在CGI中输入域名会导致“eval中存在不安全的依赖”。

3
在使用Perl和污点模式的CGI脚本中,给定以下内容,我无法通过以下步骤。
tail /etc/httpd/logs/error_log 
        /usr/local/share/perl5/Net/DNS/Dig.pm line 906 (#1)
    (F)您尝试执行污点机制不喜欢的操作。
    当您正在运行setuid或setgid时,或者当您显式指定-T时,污点机制会打开。 污点机制标记直接或间接派生自用户的所有数据,认为用户不值得信任。 如果在“危险”操作中使用了任何此类数据,则会出现此错误。 有关更多信息,请参见perlsec。
[Mon Jan 6 16:24:21 2014] dig.cgi:在使用-T开关运行时,在eval中存在不安全的依赖项,位于/usr/local/share/perl5/Net/DNS/Dig.pm第906行。

代码:

#!/usr/bin/perl -wT
use warnings;
use strict;
use IO::Socket::INET;
use Net::DNS::Dig;
use CGI;

$ENV{"PATH"} = ""; # Latest attempted fix

my $q      = CGI->new;
my $domain = $q->param('domain');

if ( $domain =~ /(^\w+)\.(\w+\.?\w+\.?\w+)$/ ) {
    $domain = "$1\.$2";
}
else {
    warn("TAINTED DATA SENT BY $ENV{'REMOTE_ADDR'}: $domain: $!");
    $domain = "";    # successful match did not occur
}

my $dig = new Net::DNS::Dig(
    Timeout   => 15,        # default
    Class     => 'IN',      # default
    PeerAddr  => $domain,
    PeerPort  => 53,        # default
    Proto     => 'UDP',     # default
    Recursion => 1,         # default
);

my @result = $dig->for( $domain, 'NS' )->to_text->rdata();
@result = sort @result;
print @result;

我通常使用Data::Validate::Domain来检查“有效”的域名,但无法以不出现污染变量错误的方式部署它。
我阅读到,为了使变量不受污染,您必须通过具有捕获组的regex并将捕获组连接起来进行传递。所以我部署了$domain =~ /(^\w+)\.(\w+\.?\w+\.?\w+)$/。如此处所示,这不是用于消毒域名并覆盖所有可能域名的最佳regex,但它符合我的需求。不幸的是,我的脚本仍然产生了污染失败,我无法弄清楚原因。

Regexp-Common没有提供域名regex,并且模块似乎无法与解除污染的变量一起使用,所以现在我很失落。

如何使这个东西通过污点检查?


我认为问题不在你的代码上(你对$domain进行了去污),那个模块可能只是无法使用严格模式。 - ThisSuitIsBlackNot
Scalar::Util qw(tainted) 证明了我的变量没有被污染,所以肯定是该模块以某种形式存在的问题。感谢 @Palec 的帮助和介绍新模块(对我来说)。 - MattSizzle
2个回答

2

$domain未被污染

我已验证您的$domain未被污染。在我看来,这是您使用的唯一可能会被污染的变量。

perl -T <(cat <<'EOF'
use Scalar::Util qw(tainted);
sub p_t($) {
    if (tainted $_[0]) {
        print "Tainted\n";
    } else {
        print "Not tainted\n";
    }
}
my $domain = shift;
p_t($domain);
if ($domain =~ /(^\w+)\.(\w+\.?\w+\.?\w+)$/) {
    $domain = "$1\.$2";
} else {
    warn("$domain\n");
    $domain = "";
}
p_t($domain);
EOF
) abc.def

它会打印出来

Tainted
Not tainted

Net::DNS::Dig 的作用

请参见 Net::DNS::Dig 第906行。这是 to_text 函数的起始位置。

sub to_text {
    my $self = shift;
    my $d = Data::Dumper->new([$self],['tobj']);
    $d->Purity(1)->Deepcopy(1)->Indent(1);
    my $tobj;
    eval $d->Dump; # line 906

new的定义中,我知道$self只是一个哈希引用,包含来自new参数和构造函数中填充的其他值。由$d->Dump产生的评估代码将$tobj设置为$self的深层副本(Deepcopy(1)),其中正确设置了自身引用(Purity(1))和基本的漂亮打印(Indent(1))。

问题在哪里,如何调试

从我对&Net::DNS::Dig::to_text的了解来看,至少有一个污染的项在$self中。因此,您有一种简单的方法进一步调试您的问题:在脚本中构建$dig对象后,检查其中哪些项目被污染。您可以使用print Data::Dumper::Dump($dig);将整个结构转储到stdout中,这与评估代码大致相同,并使用&Scalar::Util::tainted检查可疑项。
我不知道这离让Net::DNS::Dig在污点模式下工作还有多远。我没有使用它,我只是好奇想找出问题所在。由于您已经以其他方式解决了您的问题,因此我将其保留在这个阶段,让其他人继续调试该问题。

1
作为对这个问题的解决方案,如果将来有人遇到它,确实是我使用的模块导致了“taint”检查失败。这教会了我一个重要的课程,即在CGI环境中信任模块。我换成了Net::DNS,因为我认为它不会遇到这个问题,果然没有。我的代码如下,供参考,以防有人想要完成我所设定的目标:查找定义域名内部区域文件中的名称服务器。
#!/usr/bin/perl -wT
use warnings;
use strict;
use IO::Socket::INET;
use Net::DNS;
use CGI;

$ENV{"PATH"} = ""; // Latest attempted fix

my $q      = CGI->new;
my $domain = $q->param('domain');
my @result;

if ( $domain =~ /(^\w+)\.(\w+\.?\w+\.?\w+)$/ ) {
    $domain = "$1\.$2";
}
else {
    warn("TAINTED DATA SENT BY $ENV{'REMOTE_ADDR'}: $domain: $!");
    $domain = "";    # successful match did not occur
}

my $ip = inet_ntoa(inet_aton($domain));

my $res   = Net::DNS::Resolver->new(
    nameservers => [($ip)],
);
                        my $query = $res->query($domain, "NS");

                        if ($query) {
                                foreach my $rr (grep { $_->type eq 'NS' } $query->answer) {
                                push(@result, $rr->nsdname);
                                }
                        }
                        else {
                                warn "query failed: ", $res->errorstring, "\n";
                        }

@result = sort @result;
print @result;

感谢您在这件事上提供的评论帮助,以及 Stack Overflow 教给我的比我接触过的任何其他资源都多的知识。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接