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
使字符串可以安全地用作命令的单个参数.
确定任何两个听起来相似的 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\n0 - 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\n0 - 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\necho escapeshellarg("something here") . "\\n";\necho escapeshellarg("\'something here\'") . "\\n";\necho escapeshellarg("\\"something here\\"") . "\\n";\n
Run Code Online (Sandbox Code Playgroud)\n\nWindows 输出:
\n\n"something here"\n"\'something here\'"\n" something here "\n
Run Code Online (Sandbox Code Playgroud)\n\nLinux 输出:
\n\n\'something here\'\n\'\'\\\'\'something here\'\\\'\'\'\n\'"something here"\'\n
Run Code Online (Sandbox Code Playgroud)\n\nWindows 上的 PHP escapeshellarg() 用双引号 " 字符包围字符串,而 Linux 上使用单引号 \' 字符。Windows 上的 PHP 完全用空格替换内部双引号(在某些情况下这可能是一个问题)。PHP Linux 上有点特意转义单引号和反斜杠 \\ 在 Windows 上转义 \\\\。Windows 上的 PHP escapeshellarg() 还会用空格替换 ! 和 % 字符。所有平台都用空格替换 \\0 。
\n\n请注意,PHP 版本之间的行为不一定一致,并且 PHP 文档并不总是反映现实。编写快速脚本或阅读 PHP 源代码是了解幕后发生情况的两种方法。
\n来自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)
两者都做类似的事情,只是取决于你如何处理你的逻辑,确保你的规范化和验证输入,然后直接传递给这些方法,以提高安全性.