如何为 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 ax
。 openssl 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 方面的专家,所以我可能犯了一些愚蠢的命令语法错误或其他错误。使用前请仔细测试!
centos-6.8 perl,v5.10.1 (*) 为 x86_64-linux-thread-multi
构建这个问题源自这个
我有这些代码片段:
. . .
$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()
, andharness()
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 ax
。 openssl 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 方面的专家,所以我可能犯了一些愚蠢的命令语法错误或其他错误。使用前请仔细测试!