如何以nobody用户的身份在CGI程序中运行shell命令?



我想在CGI程序(用Perl编写)中运行shell命令。我的程序没有root权限。它以无名之辈的身份运行。我想使用以下代码:

use strict;
system <<'EEE';
awk '{a[$1]+=$2;b[$1]+=$3}END{for(i in a)print i, a[i], b[i]|"sort -nk 3"}' s.txt
EEE

我可以在命令行中用perl成功地运行我的代码,但不能作为CGI程序。

根据你问题中的代码,至少有四种失败的可能性。

  1. nobody用户没有权限执行程序。
  2. 您的问题中的Perl代码没有shebang (#!)行。您正在尝试运行awk,因此我假设您正在某种形式的Unix上运行。如果你的代码缺少这一行,那么你的操作系统不知道如何运行你的程序。
  3. 文件s.txt不在执行程序的工作目录中,或者nobody用户无法读取该文件
  4. 无论什么原因,awk无法通过执行程序环境的PATH访问。

要快速诊断此类低级问题,请尝试在浏览器中显示所有错误输出。一种方法是在代码的shebang行后面添加以下代码:

BEGIN {
    print "Content-type: text/plainnn";
    open STDERR, ">&", *STDOUT or print "$0: dup: $!";
}

输出将呈现为纯文本而不是HTML,但这是查看程序输出的临时措施。通过将其包装在BEGIN块中,代码在解析后立即执行。重定向STDERR意味着您的浏览器也可以将任何内容写入标准输出。

另一种方法是使用CGI::Carp模块。
use CGI::Carp 'fatalsToBrowser';

这样,错误会记录在浏览器和web服务器的错误日志中。

如果您仍然看到来自服务器的500系列错误,则问题发生在较低级别:可能是启动perl失败。去检查服务器的错误日志。一旦你的程序开始执行,你可以删除这个临时的错误输出重定向。

最后,我建议把你的程序改成

#! /usr/bin/perl -T
BEGIN { print "Content-type: text/plainnn"; }
use strict;
use warnings;
$ENV{PATH} = "/bin:/usr/bin";
my $input = "/path/to/your/s.txt";
my $buckets = <<'EOProgram'
{ a[$1] += $2; b[$1] += $3 }
END { for (i in a) print i, a[i], b[i] }
EOProgram
open STDIN, "-|", "awk", $buckets, $input or die "$0: open: $!";
exec "sort", "-nk", 3                     or die "$0: exec: $!";

-T开关启用了一种称为污染模式的安全数据流分析,它可以防止对系统操作(如openexec等)使用未经消毒的输入,攻击者(或提供意外输入的良性用户)可能利用这些输入来损害您的系统。您应该始终将-T添加到CGI程序和代表其他用户运行的任何其他代码中。

考虑到awk程序的性质,text/plain的内容类型似乎是合理的。请尽快输出

在启用了污染模式的情况下,明确指定PATH环境变量的值。如果您坚持使用您的程序继承的任何不受信任的路径,那么尝试运行外部程序将会失败。

确定输入的完整路径。这将消除意外。

使用openexec的多参数形式消除了shell及其参数解析。(为了完整起见,system也有类似的多参数形式。)是的,以这种方式编写代码可能意味着更慎重一些(比如自己分解参数和设置管道),但它也避免了令人讨厌的意外。

我确信nobody是允许运行shell命令的。问题是nobody没有权限打开文件s.txt

s.txt添加每个人的读权限,并为s.txt之前的每个目录添加每个人的执行权限。

我建议找出awk的完整限定路径并直接指定它。启动httpd的人可能在其$ENV{path}中有一个非常小的路径。显示$ENV{PATH}我猜会显示这个

这是一件好事,我不会修改路径,而只是指定路径/usr/bin/awk或其他。

如果您有shell访问权限并且它可以工作,请键入'which awk'来查找。

我可以成功地运行我的代码Perl文件,而不是cgi文件。

你在哪个web服务器下运行?例如,apache需要打印一个CGI头,即print "Content-type: text/plain; charset=utf-8nn",或

use CGI;
my $q = CGI->new();
print $q->header('text/html');

(见CGI)

Apache将在日志(error.log)中抱怨"脚本头过早结束",如果我说的是这种情况。

您可以直接内联,而不必fork到另一个进程…

if ( open my $fh, '<', 's.txt' ) {
    my %data;
    while (<$fh>) {
        my ($c1,$c2,$c3) = split;
        $data{a}{$c1} += $c2;
        $data{b}{$c1} += $c3;
    }
    foreach ( sort { $data{b}{$a} <=> $data{b}{$b} } keys %{ $data{b} } ) {
        print "$_ $data{a}{$_} $data{b}{$_}n";
    }
} else {
    warn "Unable to open s.txt: $!n";
}

最新更新