是否可以在没有其他HTTP请求的情况下将POST参数发送到CGI脚本?

时间:2009-12-05 20:52:50

标签: perl http cgi

我正在尝试从另一个Perl模块在当前环境中运行CGI脚本。所有这些都适用于使用标准系统调用GET请求。 POST也很好,直到参数列表太长,然后它们被切断。

是否有人遇到此问题,或者有其他方法尝试此问题?

为清楚起见,以下略有简化。还有更多的错误检查等。

对于 GET请求 POST请求没有参数,我执行以下操作:

# $query is a CGI object.
my $perl = $^X;
my $cgi  = $cgi_script_location; # /path/file.cgi
system {$perl} $cgi;
  • 参数通过 QUERY_STRING环境变量。
  • STDOUT被呼叫捕获 脚本所以无论CGI脚本如何 打印行为正常。
  • 此部分有效。

对于带参数的POST请求,以下方法有效,但似乎限制了我的可用查询长度:

# $query is a CGI object.
my $perl = $^X;
my $cgi  = $cgi_script_location; # /path/file.cgi

# Gather parameters into a URL-escaped string suitable 
# to pass to a CGI script ran from the command line.
# Null characters are handled properly.
# e.g., param1=This%20is%20a%20string&param2=42&... etc.
# This works.
my $param_string = $self->get_current_param_string();

# Various ways to do this, but system() doesn't pass any 
# parameters (different question).
# Using qx// and printing the return value works as well.
open(my $cgi_pipe, "|$perl $cgi");
print {$cgi_pipe} $param_string;
close($cgi_pipe);
  • 此方法适用于短参数列表,但如果整个命令接近1000个字符,则参数列表将被缩短。这就是我试图将参数保存到文件的原因;避免外壳限制。
  • 如果我从执行的CGI脚本中转储参数列表,我会得到如下内容:

param1=blah
... a bunch of other parameters ...
paramN=whatever
p <-- cut off after 'p'. There are more parameters.

我做过的其他事情没有帮助或工作

  • 关注CGI troubleshooting guide
  • 使用CGI-&gt; save()将参数保存到文件,将该文件传递给CGI脚本。只使用此方法读取第一个参数。

$> perl index.cgi < temp-param-file

  • 将$ param_string保存到文件中,将该文件传递给CGI脚本,如上所述。与通过命令行传递命令相同的限制;仍然被切断了。
  • 确保$CGI::POST_MAX可接受的高度(它为-1)。
  • 确保CGI的命令行处理正常。 (:未设置no_debug)
  • 使用相同的参数从命令行中运行CGI。这很有效。

信息

  • 显然,这似乎是Perl用于执行命令的shell的字符限制,但是通过将参数传递给文件无法解决。

3 个答案:

答案 0 :(得分:1)

从HTTP输入将参数作为单个字符串传递给系统是非常危险的。

来自perldoc -f system

  

如果只有一个标量参数,则检查参数是否为shell元字符,如果有,则将整个参数传递给系统的命令shell进行解析(这是Unix平台上的/ bin / sh -c,但在其他平台上有所不同)。如果参数中没有shell元字符,..

换句话说,如果我传入参数-e printf("working..."); rm -rf /;,我可以从磁盘中删除信息(如果您的Web服务器以root用户身份运行,则为所有内容)。如果您选择这样做,请务必改为呼叫system("perl", @cgi)

您遇到的参数长度问题可能是操作系统限制(在http://www.in-ulm.de/~mascheck/various/argmax/中描述):

  

有不同的方法来学习上限:

     
      
  • 命令:getconf ARG_MAX
  •   
  • 系统标题:例如ARG_MAX百分比抑制率数据SYS /] limits.h中&GT;
  •   

保存到临时文件是有风险的:多次调用CGI可能会保存到同一个文件,从而产生竞争条件,其中一个用户的参数可能被另一个用户的进程使用。

您可以尝试打开进程的文件句柄,并将参数作为标准输入传递。 open my $perl, '|', 'perl' or die; fprintf(PERL, @cgi);

答案 1 :(得分:0)

我不想这样做,但我已经采用了最直接的方法并且它有效。我试图让环境认为请求方法是GET,以便被调用的CGI脚本将从它期望的QUERY_STRING环境变量中读取其输入。像这样:

$ENV{'QUERY_STRING'} = $long_parameter_string . '&' . $ENV{'QUERY_STRING'};
$ENV{'REQUEST_METHOD'} = 'GET';

system {$perl_exec} $cgi_script;

我担心这可能导致的潜在问题,但我想不出这会带来什么危害,到目前为止效果还不错。但是,因为我很担心,我想如果他们看到任何潜在的问题,我会问部落:

Are there any problems handling a POST request as a GET request on the server

除非人们在上述帖子中确认或至少辩论过,否则我会将此标记为正式答案。

答案 2 :(得分:0)

事实证明,问题实际上与原始参数和拼凑在一起的参数字符串之间的Content-Length的差异有关。我没有意识到CGI模块使用原始标头中的这个值作为读取输入量的限制(有意义!)。显然,我正在做的额外逃避是添加一些角色。

我的解决方案的诀窍就是将我将要传递的参数字符串拼凑在一起并修改CGI模块将检查的环境变量,以确定内容长度等于。

这是最终的工作代码:

use CGI::Util qw(escape);

my $params;

foreach my $param (sort $query->param) {
 my $escaped_param  = escape($param);

 foreach my $value ($query->param($param)) {
  $params .= "$escaped_param=" . escape("$value") . "&";
 }
}

foreach (keys %{$query->{'.fieldnames'}}) {
 $params .= ".cgifields=" . escape("$_") . "&";
}

# This is the trick.
$ENV{'CONTENT_LENGTH'} = length($params);

open(my $cgi_pipe, "| $perl $cgi_script") || die("Cannot fork CGI: $!");
local $SIG{PIPE} = sub { warn "spooler pipe broke" };

print {$cgi_pipe} $params;

warn("param chars: " . length($params));

close($cgi_pipe) || warn "Error: CGI exited with value $?";

感谢您的帮助!