escapeshellarg和escapeshellcmd有什么区别?

时间:2009-12-10 15:06:12

标签: php shell process exec subprocess

PHP有两个密切相关的函数,escapeshellarg()escapeshellcmd()。他们似乎都做了类似的事情,即帮助使字符串在system() / exec() /等中使用更安全。

我应该使用哪一个?我只是希望能够接受一些用户输入并在其上运行命令,而不是让一切都爆炸。如果PHP有一个exec-type-function,它接受了一个绕过shell的字符串数组(比如argv),我会使用它。与Python的subprocess.call()函数类似。

4 个答案:

答案 0 :(得分:90)

通常,您需要使用escapeshellarg,使shell命令的单个参数安全。原因如下:

假设您需要获取目录中的文件列表。你想出了以下内容:

$path  = 'path/to/directory'; // From user input

$files = shell_exec('ls '.$path);
// Executes `ls path/to/directory`

(这是一种不好的做法,但为了说明我的承诺)

这对于这条路径来说“很棒”,但假设给出的路径更危险:

$path  = 'path; rm -rf /';

$files = shell_exec('ls '.$path);
// Executes `ls path`, then `rm -rf /`;

由于给定的路径未使用,因此可能会运行任何命令。我们可以使用escapeshell*方法来阻止这种情况。

首先,使用escapeshellcmd

$path = 'path; rm -rf /';

$files = shell_exec(escapeshellcmd('ls '.$path));
// Executes `ls path\; rm -rf /`;

此方法仅转义可能导致运行多个命令的字符,因此虽然它可以避免主要的安全风险,但它仍然可以导致传入多个参数。

现在,使用escapeshellarg

$path = 'path; rm -rf /';

$files = shell_exec('ls '.escapeshellarg($path));
// Executes `ls 'path; rm -rf /'`;

这给了我们想要的结果。您会注意到它引用了整个参数,因此不需要对单个空格等进行转义。如果论证本身有引号,则会引用它们。

总而言之,escapeshellcmd确保字符串只有一个命令,而escapeshellarg使字符串可以安全地用作命令的单个参数。

答案 1 :(得分:7)

来自http://ie2.php.net/manual/en/function.escapeshellarg.php

  

escapeshellarg()添加单引号   围绕一个字符串和引号/转义任何   现有的单引号允许您   将字符串直接传递给shell   功能,并将其视为一个   单一的安全论据。

escapeshellarg,如其名称所示,用作传递shell参数。例如,您要列出当前目录,

$dir = ".";
system('ls '.escapeshellarg($dir));
escapeshellcmd('ls $dir');

两者都做类似的事情,只是取决于你如何处理你的逻辑,确保你的规范化和验证输入,然后直接传递给这些方法以获得更好的安全性。

答案 2 :(得分:4)

PHP文档阐明了不同之处:

<强> escapeshellcmd

  

以下字符前面加一个反斜杠:#&amp ;;` | *?〜&lt;&gt; ^()[] {} $ \,   \ x0A和\ xFF。 '和'只有在没有配对的情况下才会被转义   Windows,所有这些字符加%被替换为空格。

<强> escapeshellarg

  

在字符串周围添加单引号,并引用/转义任何现有字符串   单引号允许您将字符串直接传递给shell   功能,并将其视为一个安全的论点。

来源:

http://www.php.net/manual/en/function.escapeshellcmd.php http://www.php.net/manual/en/function.escapeshellarg.php

答案 3 :(得分:4)

确定任何两个声音相似的PHP函数之间差异的简单解决方案是在PHP中编写一个快速命令行脚本,输出整个可能的搜索空间并显示差异(在这种情况下,比较256个值):

<?php
    for ($x = 0; $x < 256; $x++)
    {
        if (chr($x) !== escapeshellcmd(chr($x)))  echo $x . " - cmd:  " . chr($x) . " != " . escapeshellcmd(chr($x)) . "\n";
    }

    echo "\n\n";

    for ($x = 0; $x < 256; $x++)
    {
        if (chr($x) !== substr(escapeshellarg(chr($x)), 1, -1))  echo $x . " - arg:  " . chr($x) . " != " . substr(escapeshellarg(chr($x)), 1, -1) . "\n";
    }
?>

在Windows命令提示符下运行PHP 5.6以上的输出:

0 - cmd:    !=
10 - cmd:
 != ^

33 - cmd:  ! != ^!
34 - cmd:  " != ^"
35 - cmd:  # != ^#
36 - cmd:  $ != ^$
37 - cmd:  % != ^%
38 - cmd:  & != ^&
39 - cmd:  ' != ^'
40 - cmd:  ( != ^(
41 - cmd:  ) != ^)
42 - cmd:  * != ^*
59 - cmd:  ; != ^;
60 - cmd:  < != ^<
62 - cmd:  > != ^>
63 - cmd:  ? != ^?
91 - cmd:  [ != ^[
92 - cmd:  \ != ^\
93 - cmd:  ] != ^]
94 - cmd:  ^ != ^^
96 - cmd:  ` != ^`
123 - cmd:  { != ^{
124 - cmd:  | != ^|
125 - cmd:  } != ^}
126 - cmd:  ~ != ^~
255 - cmd:    != ^ 


0 - arg:    !=
33 - arg:  ! !=
34 - arg:  " !=
37 - arg:  % !=
92 - arg:  \ != \\

在PHP 5.5下为Linux输出运行相同的脚本:

0 - cmd:   !=
10 - cmd:
 != \

34 - cmd:  " != \"
35 - cmd:  # != \#
36 - cmd:  $ != \$
38 - cmd:  & != \&
39 - cmd:  ' != \'
40 - cmd:  ( != \(
41 - cmd:  ) != \)
42 - cmd:  * != \*
59 - cmd:  ; != \;
60 - cmd:  < != \<
62 - cmd:  > != \>
63 - cmd:  ? != \?
91 - cmd:  [ != \[
92 - cmd:  \ != \\
93 - cmd:  ] != \]
94 - cmd:  ^ != \^
96 - cmd:  ` != \`
123 - cmd:  { != \{
124 - cmd:  | != \|
125 - cmd:  } != \}
126 - cmd:  ~ != \~
128 - cmd:   !=
...
255 - cmd:  ÿ !=


0 - arg:   !=
39 - arg:  ' != '\''
128 - arg:   !=
...
255 - arg:  ÿ !=

主要区别在于Windows下的PHP escapeshellcmd()使用插入符号^而不是反斜杠\前缀字符。 Linux中从chr(128)到chr(255)对于escapeshellcmd()和escapeshellarg()的奇怪之处可以通过使用被删除,截断或误解的无效UTF-8代码点来解释。

另外值得注意的是,escapeshellarg()可以逃避更少的字符并仍然完成工作。

就整体系统和应用程序的安全性而言,最好使用escapeshellarg()并单独转义由用户输入组成的每个参数。

最后一个例子:

echo escapeshellarg("something here") . "\n";
echo escapeshellarg("'something here'") . "\n";
echo escapeshellarg("\"something here\"") . "\n";

Windows输出:

"something here"
"'something here'"
" something here "

Linux输出:

'something here'
''\''something here'\'''
'"something here"'
Windows上的PHP escapeshellarg()用双引号&#34;围绕字符串。 Linux使用单引号时的字符&#39;字符。 Windows上的PHP完全用空格替换内部双引号(在某些情况下可能会出现问题)。 Linux上的PHP有点脱离单引号和反斜杠\在Windows上转义\\。 Windows上的PHP escapeshellarg()也取代了!和带空格的%字符。所有平台都用空格替换\ 0。

请注意,PHP版本之间的行为不一定一致,PHP文档并不总是反映现实。编写快速脚本或阅读PHP源代码是了解幕后发生情况的两种方法。