escapeshellarg和escapeshellcmd有什么区别?

Ror*_*ory 37 php shell subprocess process exec

PHP有两个密切相关的函数,escapeshellarg()escapeshellcmd().他们似乎都做类似的事情,就是帮助一个字符串更安全的使用system()/ exec()的/ etc.

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

Ada*_*dam 95

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

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

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

$files = shell_exec('ls '.$path);
// Executes `ls path/to/directory`
Run Code Online (Sandbox Code Playgroud)

(这是一个不好的方法,但为了插图熊我)

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

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

$files = shell_exec('ls '.$path);
// Executes `ls path`, then `rm -rf /`;
Run Code Online (Sandbox Code Playgroud)

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

首先,使用escapeshellcmd:

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

$files = shell_exec(escapeshellcmd('ls '.$path));
// Executes `ls path\; rm -rf /`;
Run Code Online (Sandbox Code Playgroud)

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

现在,使用escapeshellarg:

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

$files = shell_exec('ls '.escapeshellarg($path));
// Executes `ls 'path; rm -rf /'`;
Run Code Online (Sandbox Code Playgroud)

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

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


Cub*_*oft 7

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

\n\n
<?php\n    for ($x = 0; $x < 256; $x++)\n    {\n        if (chr($x) !== escapeshellcmd(chr($x)))  echo $x . " - cmd:  " . chr($x) . " != " . escapeshellcmd(chr($x)) . "\\n";\n    }\n\n    echo "\\n\\n";\n\n    for ($x = 0; $x < 256; $x++)\n    {\n        if (chr($x) !== substr(escapeshellarg(chr($x)), 1, -1))  echo $x . " - arg:  " . chr($x) . " != " . substr(escapeshellarg(chr($x)), 1, -1) . "\\n";\n    }\n?>\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
0 - cmd:    !=\n10 - cmd:\n != ^\n\n33 - cmd:  ! != ^!\n34 - cmd:  " != ^"\n35 - cmd:  # != ^#\n36 - cmd:  $ != ^$\n37 - cmd:  % != ^%\n38 - cmd:  & != ^&\n39 - cmd:  \' != ^\'\n40 - cmd:  ( != ^(\n41 - cmd:  ) != ^)\n42 - cmd:  * != ^*\n59 - cmd:  ; != ^;\n60 - cmd:  < != ^<\n62 - cmd:  > != ^>\n63 - cmd:  ? != ^?\n91 - cmd:  [ != ^[\n92 - cmd:  \\ != ^\\\n93 - cmd:  ] != ^]\n94 - cmd:  ^ != ^^\n96 - cmd:  ` != ^`\n123 - cmd:  { != ^{\n124 - cmd:  | != ^|\n125 - cmd:  } != ^}\n126 - cmd:  ~ != ^~\n255 - cmd:  \xc2\xa0 != ^\xc2\xa0\n\n\n0 - arg:    !=\n33 - arg:  ! !=\n34 - arg:  " !=\n37 - arg:  % !=\n92 - arg:  \\ != \\\\\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
0 - cmd:   !=\n10 - cmd:\n != \\\n\n34 - cmd:  " != \\"\n35 - cmd:  # != \\#\n36 - cmd:  $ != \\$\n38 - cmd:  & != \\&\n39 - cmd:  \' != \\\'\n40 - cmd:  ( != \\(\n41 - cmd:  ) != \\)\n42 - cmd:  * != \\*\n59 - cmd:  ; != \\;\n60 - cmd:  < != \\<\n62 - cmd:  > != \\>\n63 - cmd:  ? != \\?\n91 - cmd:  [ != \\[\n92 - cmd:  \\ != \\\\\n93 - cmd:  ] != \\]\n94 - cmd:  ^ != \\^\n96 - cmd:  ` != \\`\n123 - cmd:  { != \\{\n124 - cmd:  | != \\|\n125 - cmd:  } != \\}\n126 - cmd:  ~ != \\~\n128 - cmd:   !=\n...\n255 - cmd:  \xc3\xbf !=\n\n\n0 - arg:   !=\n39 - arg:  \' != \'\\\'\'\n128 - arg:   !=\n...\n255 - arg:  \xc3\xbf !=\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

另外值得注意的是 escapeshellarg() 转义的字符要少得多,但仍然可以完成工作。

\n\n

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

\n\n

最后一个例子:

\n\n
echo escapeshellarg("something here") . "\\n";\necho escapeshellarg("\'something here\'") . "\\n";\necho escapeshellarg("\\"something here\\"") . "\\n";\n
Run Code Online (Sandbox Code Playgroud)\n\n

Windows 输出:

\n\n
"something here"\n"\'something here\'"\n" something here "\n
Run Code Online (Sandbox Code Playgroud)\n\n

Linux 输出:

\n\n
\'something here\'\n\'\'\\\'\'something here\'\\\'\'\'\n\'"something here"\'\n
Run Code Online (Sandbox Code Playgroud)\n\n

Windows 上的 PHP escapeshellarg() 用双引号 " 字符包围字符串,而 Linux 上使用单引号 \' 字符。Windows 上的 PHP 完全用空格替换内部双引号(在某些情况下这可能是一个问题)。PHP Linux 上有点特意转义单引号和反斜杠 \\ 在 Windows 上转义 \\\\。Windows 上的 PHP escapeshellarg() 还会用空格替换 ! 和 % 字符。所有平台都用空格替换 \\0 。

\n\n

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

\n


Jay*_*eng 5

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

escapeshellarg()在字符串周围添加单引号,并引用/转义任何现有的单引号,允许您将字符串直接传递给shell函数并将其视为单个安全参数.

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

$dir = ".";
system('ls '.escapeshellarg($dir));
escapeshellcmd('ls $dir');
Run Code Online (Sandbox Code Playgroud)

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

  • 这不是答案.问题是有什么不同. (41认同)
  • 真正的问题是"我应该使用哪一个?".我想杰伊回答了这个问题. (2认同)
  • 我不会说他们做“类似的事情”,请检查亚当的答案是否有真正的区别。 (2认同)