【问题标题】:Is it possible to send POST parameters to a CGI script without another HTTP request?是否可以在没有另一个 HTTP 请求的情况下将 POST 参数发送到 CGI 脚本?
【发布时间】:2010-12-23 15:07:35
【问题描述】:

我正在尝试从另一个 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->save() 将参数保存到文件中,将该文件传递给 CGI 脚本。使用此方法仅读取第一个参数。

$&gt; perl index.cgi &lt; temp-param-file

  • 将 $param_string 保存到一个文件中,将该文件传递给 CGI 脚本,就像上面一样。与通过命令行传递命令的限制相同;仍然被切断。
  • 确保$CGI::POST_MAX 的高值可以接受(为-1)。
  • 确保 CGI 的命令行处理工作正常。 (:no_debug 没有设置)
  • 使用相同的参数从命令行运行 CGI。这行得通。

潜在客户

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

【问题讨论】:

    标签: perl http cgi


    【解决方案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 在例如

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

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

    【讨论】:

    • 函数是open,而不是fopen,你应该始终使用3参数形式(例如open my $perl, '|', 'perl' or die)。
    • 我实际上是在使用这种形式的系统来调用 GET 请求,我面前没有代码,所以我错过了。不过谢谢,你是对的。正如我在前导部分中列出的那样,我知道 shell 可能存在字符限制,因此我尝试将参数保存到文件并将该文件作为输入传递。顺便说一下,我使用用户的唯一 ID 加上一个大的随机数来命名文件,尽管我知道竞争条件的可能性很小。如果它确实有效,我可能会添加其他东西来进一步缓解这种情况。
    • 就管道而言,这与我对第二个代码块中的 open ... print .. close 命令所做的事情不同吗?参数以这种方式向上传递,直到它们开始被切断。似乎可能存在我不知道的 STDIN 缓冲区限制。
    • @James van Dyke:关于管道,我在第一次通读时一定错过了 - 我想我第一次轻而易举地通过了那个块,认为它是被调用的 perl 脚本,而不是调用脚本。看起来确实是一样的。您是否查看了调用的脚本以确保它没有对输入参数做一些有趣的事情?
    • @atk:它正在做标准 CGI 模块正在做的任何事情。当网络服务器直接调用 CGI 并且当我在命令行运行它并输入大量参数(如 CGI 文档本身示例)时,CGI 可以工作:perldoc.perl.org/CGI.html#DEBUGGING 管道输入到脚本时似乎需要一些限制或特殊咒语在 Perl 中。
    【解决方案2】:

    我不想这样做,但我采用了最直接的方法并且它有效。我欺骗环境认为请求方法是 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

    在人们确认或至少在上述帖子中进行辩论之前,我会将其标记为官方答案。

    【讨论】:

    • 我还没有发现这个问题,但是我已经在这里发布了这个问题的真正答案。
    【解决方案3】:

    原来问题其实和我拼凑的原始参数和参数串的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 $?";
    

    感谢大家的帮助!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-09-15
      • 2013-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多