escapeshellcmd

(PHP 4, PHP 5, PHP 7, PHP 8)

escapeshellcmd转义 shell 元字符

描述

escapeshellcmd(string $command): string

escapeshellcmd() 转义字符串中任何可能被用于欺骗 shell 命令执行任意命令的字符。此函数应用于确保来自用户输入的任何数据在传递给 exec()system() 函数或 反引号运算符 之前被转义。

以下字符前会加一个反斜杠:&#;`|*?~<>^()[]{}$\\x0A\xFF'" 只有在没有成对出现时才会被转义。在 Windows 上,所有这些字符加上 %! 前面都会加一个插入符号 (^)。

参数

command

将要转义的命令。

返回值

转义后的字符串。

范例

范例 #1 escapeshellcmd() 范例

<?php
// 我们故意允许任意数量的参数。
$command = './configure '.$_POST['configure_options'];

$escaped_command = escapeshellcmd($command);

system($escaped_command);
?>

注释

警告

escapeshellcmd() 应该应用于整个命令字符串,它仍然允许攻击者传递任意数量的参数。为了转义单个参数,应该使用 escapeshellarg()

警告

空格不会被 escapeshellcmd() 转义,这在 Windows 上可能会有问题,例如:C:\Program Files\ProgramName\program.exe。这可以通过以下代码片段来缓解

<?php
$cmd
= preg_replace('`(?<!^) `', '^ ', escapeshellcmd($cmd));

参见

添加注释

用户贡献的注释 8 个注释

9
nicholas at nicholaswilson dot me dot uk
13 年前
需要注意的是,使用 echo 存在一个怪癖。如果您要执行的命令需要从 STDIN 获取输入,通常您会这样做

<?php $output = shell_exec("echo $input | /the/command"); ?>

不幸的是,这是一个 *坏主意*,它会使您的脚本不可移植,并在某些系统上产生一个非常难以追踪的错误。根据服务器的设置方式,/bin/sh 会调用 /bin/bash 或 /bin/dash,它们具有非常不同的 echo 版本。永远不要使用 echo;而是使用 printf,它是一致的。如何为 printf 转义?执行以下操作

<?php
$input
= '要传递给命令的字符串 *完全*';
// 只转义 PHP 解析器需要转义的内容;我们希望
// PHP 在其缓冲区中保存的字符串数据被
// 完全传递到命令的 stdin 缓冲区。
$cmd = str_replace(array('\\', '%'), array('\\\\', '%%'), $input);
$cmd = escapeshellarg($cmd);

$output = shell_exec("printf $cmd | /path/to/command");
?>

对于谨慎的人来说,这个压力测试验证了 shell 转义和 printf 自己的转义是否都正确处理。您可以放心地使用它!

<?php

$test
= 'bash 解释的字符串,空格:# & ; ` | * ? ~ < > ^ ( ) [ ] { } $ \ \x0A \xFF. \' " %'.PHP_EOL.
'bash 解释的字符串,无空格:#&;`|*?~<>^()[]{}$\\x0A\xFF.\'\"%'.PHP_EOL.
'bash 解释的字符串,以反斜杠开头:\# \& \; \` \| \* \? \~ \< \> \^ \( \) \[ \] \{ \} \$ \\\ \\\x0A \\\xFF. \\\' \" \%'.PHP_EOL.
'printf 代码:% \ (用于构成 %.0#-*+d,或 \\ \a \b \f \n \r \t \v \" \? \062 \0062 \x032 \u0032 和 \U00000032)';

echo
"我们正在测试的字符串是:".PHP_EOL.$test.PHP_EOL;
$cmd = $test;
$cmd = str_replace(array('\\', '%'), array('\\\\', '%%'), $test);
$cmd = escapeshellarg($cmd);

echo
PHP_EOL."这是使用给定转义机制的输出:".PHP_EOL;
echo `
printf $cmd | cat`.PHP_EOL;

echo
PHP_EOL."它们应该完全匹配".PHP_EOL;
?>
0
carlos at wfmh dot org dot pl dot REMOVE dot COM
14 年前
注意它不会转义 !(感叹号)。因此,如果您想 i.e. printf() 命令以备将来在 shell 中使用(即通过粘贴到控制台),您需要转义所有感叹号,否则 shell 会尝试将 ! 作为历史引用处理。这种方法就足够了

<?php $scaped = str_replace('!', '\!', escapeshellarg( $str ) ); ?>
0
docey
19 年前
引用命令的主要原因是它不能连接多个命令。我不知道这是否是正确的语法,但请记住,这会导致一些安全漏洞。以下是如何确切地知道您试图侵入什么的方法。

通常,任何 Linux 用户都可以查看几乎任何目录,因此
ls / -als 将打印 Linux 文件系统中任何文件(包括其大小、安全性和隐藏文件)的完整列表。

现在输出只会被 PHP 知道,除非 PHP 脚本开始打印它,否则用户将无法看到这些数据。就像 passtru 做的那样!但一位优秀的 PHP 程序员知道,除非万不得已,否则永远不要使用 passtru。

但是,如果你能将来自 ls 的输出,从同一个命令行重定向到 webroot 中的一个文件,大多数 webserver 仍然默认将他们的 base-webroot 设置为 /var/www/,所以将它存储在文本文件中以便稍后下载,你只需一边喝咖啡,一边检查哪些文件可以被 PHP 安全模式读取,然后简单地使用 cp 命令将这些文件复制到 webroot 并下载到你的硬盘上。如果没有文件列表,你只能猜测从哪里复制!这比猜测 root 密码更难。

所以,如果第一个命令被引用,则由于语法错误,无法附加另一个命令。想想一旦你获得了文件系统中所有文件的完整列表,你可以做多少事情。包括通过 NFS 和其他方式挂载的那些。安全始于隐藏大门。

另一个用于挂起 webserver 的好命令是“php <?php while(true){ exec('ls / -als'); }; ?>”。这会不断地在整个文件系统上创建文件列表,这不仅会占用硬盘的 bussy,还会占用必须存储返回列表的内存和 CPU。因此,请记住,并非所有来自用户的命令都可以被盲目使用。

实际上,永远不要接受来自外部来源的任何命令,只有经过验证的内置预定义命令才能执行。
0
trisk at earthling dot net
19 年前
此函数无法按 php.net 示例中所示工作。

如果你将编码后的文件名放在双引号中,正如他们建议的那样,那么它将在文件名中的某些字符(例如与号)处断裂。

例如,如果你有一个名为“foo & bar.jpg”的文件,并且你对此文件使用此函数,那么你使用双引号的结果文件名将产生以下结果,而无法找到

"foo \& bar.jpg"

如果你需要一个包含空格的单个参数,那么不要将此函数与添加的双引号一起使用,使用 escapeshellarg(),它将整个字符串包含在单引号中。

我不明白此特定函数的用途。我看不出它有什么用处,除非你将其传递给另一个函数并将空格“ ”转换为“\ ”,这将允许你直接在命令行上使用该字符串。
-2
abennett at clarku dot edu
18 年前
我有一个 php 脚本,需要通过 exec 将用户名和密码传递给一个 perl 脚本。问题是有效密码字符被转义了……

这是我编写的一个小 perl 函数来修复它。

sub unescape_string {
my $string = shift;
# 所有这些插值正则表达式都很慢,所以如果字符串中没有
# 反斜杠,就不要费心了
# index() 比正则表达式快
if ( ! index($string,'\\\\') ) {
return $string;
}
my @characters = ('#', '&', ';', '`', '|', '*', '?', '~', '<', '>', '^', '(', ')',
'[', ']', '{', '}', '$', '\\', ',', ' ', '\x0A', '\xFF' );
my $character;
foreach $character (@characters) {
$character = quotemeta($character);
my $pattern = "\\\\(" . $character . ")";
$string =~ s/$pattern/$1/g;
}
return $string;
}

希望这对你有用。
-2
ceejay at trashfactory dot de
18 年前
好吧,伙计们,我发现当 safe_mode 打开时,强制运行 escapeshellarg 和 escapeshellcmd 来将命令传递给 exec、system 或 popen,这非常困难。

现在,我还没有找到任何可以这样传递命令的有效解决方案
cmd -arg1 -arg2 "<BLA varname=\"varvalue\" varname1=\"varvalue1\" />"

只是 arg2 的参数是看起来像 HTML 标签的字符串,其中设置了各种属性,arg2 中字符串的所有属性都被空格分隔。在 safe_mode 关闭的情况下不会发生这种情况,所以一定是 escapefunctions 之一导致了功能失效。

为了规避这个问题,我做了一个临时的解决方案,动态创建了一个脚本文件(通过 fopen),其中只包含了带有参数的整个命令,然后执行该脚本文件。我不喜欢这种解决方案,但另一方面,在该服务器上无法轻松地关闭 safe_mode。
-3
Leon
17 年前
此函数很棒——除非你需要合法地使用转义字符作为命令的一部分。下面的代码保留了单引号括起来的命令部分,但转义了其余部分,例如

"echo Never use the '<blink>' tag ; cat /etc/passwd"
变成
"echo Never use the '<blink>' tag \; cat /etc/passwd"
而不是
"echo Never use the '\<blink\>' tag \; cat /etc/passwd"

也就是说,我们确实希望转义“;”,但不希望转义 HTML 标签。为了正确安全地运行 ImageMagick 的“convert”命令,我确实需要下面的代码……

<?php

// 转义整个字符串
$cmdQ = escapeshellcmd($cmd);

// 构建引号部分的数组,以及相同的转义
preg_match_all('/\'[^\']+\'/', $cmd, $matches);
$matches = current($matches);
$quoted = array();
foreach(
$matches as $match )
$quoted[escapeshellcmd($match)] = $match;

// 用原始内容替换被单引号括起来的区域
foreach( $quoted as $search => $replace )
$cmdQ = str_replace( $search, $replace, $cmdQ );

return
$cmdQ;

?>
-4
phpcomment at reversiblemaps dot ath dot cx
12 年前
为 shell 转义字符串很棘手。

如果你的目标系统是 Windows,现在就放弃吧。Windows 甚至在如何转义内容方面也不一致。

如果是 Unix,将所有内容转换为八进制——虽然这是最难实现的,但它是最容易正确实现的——没有特殊情况。
.
To Top