我应该在 Perl 中转义 shell 参数吗?

Posted

技术标签:

【中文标题】我应该在 Perl 中转义 shell 参数吗?【英文标题】:Should I escape shell arguments in Perl? 【发布时间】:2010-10-11 19:40:30 【问题描述】:

在 Perl 中使用 system() 调用时,您是否必须转义 shell 参数,还是自动完成?

参数将是用户输入,所以我想确保这是不可利用的。

【问题讨论】:

什么意思,转义shell args?你的意思是把 \'s 放在任何像 ">" 或 "" 这样的字符之前,还是你想包括转义 $'s 这样人们就不能注入你的 Perl 变量?要不然是啥?举例说明你的意思。 如何在系统调用中添加花括号.... system("$jboss_client /subsystem=logging/size-rotating-file-handler=SAMPLE:add\(formatter=\yyyy\ \)");我总是像下面这样输出.... formatter=yyyy instaed farmatter=yyyy。你能给我一个想法来解决这个问题吗? 【参考方案1】:

如果您使用system $cmd, @args 而不是system "$cmd @args"(数组而不是字符串),那么您不必转义参数,因为没有调用任何shell(请参阅system)。即使 $cmd 包含元字符且 @args 为空,system $cmd $cmd, @args 也不会调用 shell(这已记录为 exec 的一部分)。如果 args 来自用户输入(或其他不受信任的来源),您仍将希望消除它们的污点。请参阅 perlrun 文档中的 -T 和 perlsec 文档。

如果您需要读取输出或将输入发送到命令,qxreadpipe 没有等效项。相反,请使用open my $output, "-|", $cmd, @argsopen my $input, "|-", $cmd, @args,尽管这不是可移植的,因为它需要真正的fork,这意味着仅适用于Unix……我想。也许它可以在带有模拟分支的 Windows 上运行。更好的选择是像IPC::Run 这样的东西,它还将处理管道命令到其他命令的情况,系统的多参数形式和 open 的 4 参数形式都无法处理。

【讨论】:

另外,system 'cmd' 'cmd' 总是绕过sh,即使'cmd' 包含通常由shell 解释的字符。 您应该补充一点,为什么您不必使用 "system 'cmd' @args" 转义 shell 元字符的 原因 是在这种情况下没有调用任何 shell (因为 OP 询问 shell metachars 是否会“自动”转义,但事实并非如此)。 +1,我很混乱——我以前从未听说过间接宾语语法! 我一直在测试“系统”和“打开我的 $output”解决方案,但都不允许我将 STDERR 重定向到文件,这很遗憾意味着我无法在我的场景中使用该解决方案。我认为这是按设计工作的,但想分享以防该信息对其他人有所帮助。 ***.com/questions/4413344/… @schulwitz - 他想将 stderr 重定向到一个文件,试试吧,它有效。【参考方案2】:

在 Windows 上,情况有点糟糕。基本上,所有 Win32 程序都会收到一个长的命令行字符串——shell(通常是cmd.exe)可能会首先进行一些解释,例如删除<> 重定向,但它确实 在程序的单词边界处将其拆分。每个程序都必须自己解析(如果他们愿意——有些程序不会打扰)。在 C 和 C++ 程序中,由编译器工具链提供的运行时库提供的例程通常会在调用 main() 之前执行此解析步骤。

问题是,一般来说,你不知道给定程序将如何解析它的命令行。许多程序是使用某些版本的 MSVC++(quirky parsing rules are described here)编译的,但许多其他程序是使用使用不同约定的不同编译器编译的。

cmd.exe 有自己古怪的解析规则这一事实使情况更加复杂。插入符号 (^) 被视为引用以下字符的转义字符,如果满足一系列棘手的标准,则双引号内的文本被视为引用(有关完整的血腥细节,请参阅 cmd /?)。如果您的命令包含任何奇怪的字符,cmd.exe 很容易知道哪些部分文本被“引用”,哪些部分不会与您的目标程序不同步,然后一切就乱套了。

因此,在 Windows 上转义参数最安全的方法是:

    以您正在调用的程序的命令行解析逻辑所期望的方式转义参数。 (希望您知道这个逻辑是什么;如果不知道,请尝试一些示例并猜测。) 用空格连接转义参数。 结果字符串的每个非字母数字字符前缀为^ 附加任何重定向或其他 shell 技巧(例如,使用 && 加入命令)。 使用system() 或反引号运行命令。

【讨论】:

有趣的信息 - 谢谢。这个 Unixophile 并不喜欢 Windows,但它有助于了解幕后发生的事情。 (引用的页面对插入符号的作用有点安静!它提到了它,但只是例外。不清楚它如何处理 ^\ 或 ^",例如。) 我同意乔纳森·莱弗勒的观点。这是(在我看来)处理命令行参数的糟糕方式。 我完全同意这是一个可怕的情况。尽管平心而论,但大多数可怕之处可能来自 MS 对保持向后兼容性的值得称赞的奉献。 (要了解他们的痴迷程度,请查看 Raymond Chen 的优秀博客。) @Jonathan:澄清一​​下,两个级别的编码是必要的——插入符号被 cmd.exe 看到,当它将命令行传递给正在运行的程序。该页面上的规则描述了 MSVC++ 编译的程序将如何解析其 cmd 行(即第二层解析)。 +1,感谢从我的博客到这个答案的链接:) 我同意这很糟糕,而且你已经为解决这个问题提供了很好的资源。我想知道是否有一个工具可以自动转义已经知道正确转义逻辑的东西。这样可以减少这样的反复试验。【参考方案3】:
 sub esc_chars 
  # will change, for example, a!!a to a\!\!a
     @_ =~ s/([;<>\*\|`&\$!#\(\)\[\]\\:'"])/\\$1/g;
     return @_;
  

http://www.slac.stanford.edu/slac/www/resource/how-to-use/cgi-rexx/cgi-esc.html

【讨论】:

有谁知道这个字符列表来自哪里?我找不到它 perl 文档。如果有可能,我宁愿不阅读 Perl 的 exec 函数的源代码。【参考方案4】:

如果您使用系统“$cmd @args”(字符串),那么您必须转义参数,因为调用了一个 shell。

幸运的是,对于双引号字符串,只有四个字符需要转义:

"    - double quote
$    - dollar
@    - at symbol
\    - backslash

【讨论】:

我认为 OP 是在谈论 shell 解释,而不是避免标量和数组的意外插值。【参考方案5】:

您的问题的答案非常有用。最后,我遵循了@runrig 的建议,但随后使用了核心模块 open3() 命令,这样我就可以捕获来自 STDERR 和 STDOUT 的输出。

@runrig 解决方案中使用的 open3() 示例代码见我的相关问答:Calling system commands from Perl

【讨论】:

以上是关于我应该在 Perl 中转义 shell 参数吗?的主要内容,如果未能解决你的问题,请参考以下文章

在 sed/shell 中转义 < 和 >

从 shell 中转义 sqlite3 的单引号

不需要在 javascript 中转义 innerHTML 字符?

我可以在 JavaScript 中转义 HTML 特殊字符吗?

我可以在 JavaScript 中转义 HTML 特殊字符吗?

我需要在配置文件中转义反斜杠吗?