如何为 IPC::Run 提供由用户输入设置的包含嵌入空格的参数

How does one provide IPC::Run with an argument set by user input that contains embedded whitespace

centos-6.8 perl,v5.10.1 (*) 为 x86_64-linux-thread-multi

构建

这个问题源自这个 。简而言之,我正在破解一个非常古老的 Perl 脚本,用于维护内部私有 PKI,以便默认签名哈希和密钥大小满足当前浏览器要求。

我有这些代码片段:

. . .    
$args->{keypass} = $self->getPassword("Private key password",1)
  unless $args->{keypass};    
$self->warn("# Password argument: $args->{keypass}\n") if $ENV{CSPDEBUG};
my $cmd = "-out $args->{keyfile} $args->{keysize}";
$cmd = "-des3 -passout pass:$args->{keypass} ".$cmd if defined($args->{keypass});
$self->{openssl}->cmd('genrsa',$cmd,$args);
. . .
$self->{openssl}->cmd('req',"-x509 $common_args -new -out $cacert",$args);
. . .
use IPC::Run qw( start pump finish timeout new_appender new_chunker);
. . .
sub cmd
  {
    my $self = shift;
    my $cmd = shift;
    my $cmdline = shift;
    my $args = shift;
    my $conf;
    my $cfgcmd;
. . .
    $self->{_handle}->pump while length ${$self->{_in}};
. . .

如果用户提供的密码参数值不包含白色 space,则此代码会按预期执行。如果它确实包含嵌入的白色 space,那么代码会自动失败。如果传递给 keypass 的参数与开始和结束单引号连接,那么代码同样会失败。在这两种失败的情况下,脚本仍然报告成功。

为什么?

无论用户输入是否包含 space,都需要进行哪些更改才能使此代码正常工作?

为了回答你的字面问题,让我 quote the IPC::Run manual:

"run(), start(), and harness() can all take a harness specification as input. A harness specification is either a single string to be passed to the systems' shell […] or a list of commands, io operations, and/or timers/timeouts to execute."

为了防止命令参数被 shell 解析(这是当参数包含空格时导致事情中断的原因),你不应该将它们作为单个字符串传递,而是作为对包含每个单独参数作为单个字符串的数组,如下所示:

my @cmd = ("-out", $args->{keyfile}, $args->{keysize});
unshift @cmd, ("-des3", "-passout", "pass:$args->{keypass}") if defined $args->{keypass};
# ...
my $h = start ["openssl", "genrsa", @cmd], $in, $out;  # or something equivalent

(您发布的代码似乎是通过一些自定义界面层使用 IPC::Run;由于您没有向我们展示该层的确切外观,我已将其替换为一个简单的调用到 IPC::Run::开始。)


无论如何,请注意,在命令行上传递密码通常被认为是不安全的:如果任何不受信任的用户可以 运行 在同一台服务器上编写代码(即使是在非特权帐户下),他们也可以看到密码只需 运行ning ps axopenssl manual 注意到这一点,并警告说 pass:<i>password</i> "should only be used where security is not important."

一个更安全的选择是send the password over a separate file descriptor。方便的是,IPC::Run 使这变得非常简单:

my @cmd = ("-out", $args->{keyfile}, $args->{keysize});
unshift @cmd, ("-des3", "-passout", "fd:3") if defined $args->{keypass};
# ...
my $h = start ["openssl", "genrsa", @cmd], '<', $in, '>', $out, '3<', $args->{keypass};

这里,密码是通过文件描述符3号传递的;如果需要传入多个密码,可以使用文件描述符 4、5 等。 (描述符 0、1 和 2 是标准输入、标准输出和标准错误。)

免责声明: 显然,这是所有未经测试的代码。我不是 IPC::Run 方面的专家,所以我可能犯了一些愚蠢的命令语法错误或其他错误。使用前请仔细测试!