escapeshellarg 和 escapeshellcmd 有啥区别?
Posted
技术标签:
【中文标题】escapeshellarg 和 escapeshellcmd 有啥区别?【英文标题】:What's the difference between escapeshellarg and escapeshellcmd?escapeshellarg 和 escapeshellcmd 有什么区别? 【发布时间】:2010-12-25 07:01:16 【问题描述】:php 有 2 个密切相关的函数,escapeshellarg()
和 escapeshellcmd()
。他们似乎都在做类似的事情,即帮助使字符串在system()
/exec()
/etc 中更安全地使用。
我应该使用哪一个?我只是希望能够接受一些用户输入并在其上运行命令,而不是让一切都崩溃。如果 PHP 有一个 exec-type-function,它接受一个字符串数组(如 argv),它绕过了 shell,我会使用它。类似于 Python 的subprocess.call()
函数。
【问题讨论】:
【参考方案1】:确定任何两个听起来相似的 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() 用双引号 " 字符包围字符串,而 Linux 使用单引号 ' 字符。Windows 上的 PHP 完全用空格替换内部双引号(在某些情况下这可能是一个问题) . Linux 上的 PHP 在转义单引号和反斜杠方面有点费劲,在 Windows 上 \ 转义了 \\。Windows 上的 PHP escapeshellarg() 也将 ! 和 % 字符替换为空格。所有平台都将 \0 替换为空格。
请注意,PHP 版本之间的行为不一定一致,而且 PHP 文档并不总是反映现实。编写快速脚本或阅读 PHP 源代码是了解幕后发生的事情的两种方法。
【讨论】:
【参考方案2】:通常,您会希望使用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
确保字符串安全地用作命令的单个参数。
【讨论】:
您能否编辑问题并发布这两个命令的输出,以便我可以轻松理解Executes `ls path\; rm -rf /`;
这个和这个Executes `ls path\; rm -rf /`;
的输出【参考方案3】:
PHP 文档说明了区别:
escapeshellcmd:
后面的字符以反斜杠开头:#&;`|*?~^()[]$\, \x0A 和 \xFF。 ' 和 " 仅在它们未配对时才被转义。在 Windows,所有这些字符加上 % 都被替换为空格。
escapeshellarg:
在字符串周围添加单引号并引用/转义任何现有的 单引号允许您将字符串直接传递给 shell 函数并将其视为单个安全参数。
来源:
http://www.php.net/manual/en/function.escapeshellcmd.php http://www.php.net/manual/en/function.escapeshellarg.php
【讨论】:
【参考方案4】:来自http://ie2.php.net/manual/en/function.escapeshellarg.php
escapeshellarg() 添加单引号 围绕一个字符串和引号/转义任何 现有的单引号允许您 将字符串直接传递给 shell 功能并将其视为 单个安全参数。
escapeshellarg,顾名思义,用作传递 shell 参数。比如你想列出当前目录,
$dir = ".";
system('ls '.escapeshellarg($dir));
escapeshellcmd('ls $dir');
两者都做类似的事情,只是取决于您如何处理逻辑,请确保在直接传递给这些方法之前对输入进行规范化和验证,以提高安全性。
【讨论】:
这不是答案。问题是有什么区别。 真正的问题是“我应该使用哪一个?”。我认为杰回答了这个问题。 我不会说他们做“类似的事情”,请查看亚当的答案以了解真正的区别。以上是关于escapeshellarg 和 escapeshellcmd 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章
escapeshellarg与escapeshellcmd共伤
出于安全原因,Composer escapeshellarg() 已被禁用
雷林鹏分享:CodeIgniter文件上传错误:escapeshellarg() has been disabled for security reasons