PHP有两个密切相关的函数,escapeshellarg()
和escapeshellcmd()
。他们似乎都做了类似的事情,即帮助使字符串在system()
/ exec()
/等中使用更安全。
我应该使用哪一个?我只是希望能够接受一些用户输入并在其上运行命令,而不是让一切都爆炸。如果PHP有一个exec-type-function,它接受了一个绕过shell的字符串数组(比如argv),我会使用它。与Python的subprocess.call()
函数类似。
答案 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源代码是了解幕后发生情况的两种方法。