Escapeshellcmd: примеры (PHP)
escapeshellcmd(string $command): stringescapeshellcmd - это встроенная функция PHP, предназначенная для экранирования потенциально опасных символов в строке, которая будет передана в системную оболочку (shell) для выполнения.
Функция применяется в ситуациях, когда необходимо безопасно сформировать команду для выполнения через функции вроде exec(), system(), passthru() или оператор `backtick`. Она предотвращает выполнение произвольных команд, экранируя символы, имеющие специальное значение для оболочки.
Функция принимает один обязательный параметр:
string escapeshellcmd ( string $command )
- $command - строка, содержащая команду для экранирования.
Возвращает экранированную строку команды.
Пример базового экранирования:
$command = "echo 'Hello & World' && ls -la";
echo escapeshellcmd($command);
echo 'Hello & World' &\&\& ls -la
Экранирование имени файла с пробелами:
$filename = "my file.txt; rm -rf /";
$safe_cmd = "cat " . escapeshellcmd($filename);
echo $safe_cmd;
cat my\ file.txt\;\ rm\ -rf\ /
Использование с exec():
$user_input = "./script.sh; cat /etc/passwd";
$safe_input = escapeshellcmd($user_input);
exec("ls " . $safe_input, $output);
print_r($output);
// Выведет результат команды 'ls ./script.sh\;\ cat\ /etc/passwd' // Произвольная команда не выполнится
Добавляет одинарные кавычки вокруг строки и экранирует существующие одинарные кавычки. Более надежна для экранирования отдельных аргументов команды.
echo escapeshellarg("O'Reilly's book");
'O'\''Reilly'\''s book'
escapeshellcmd применяется для экранирования всей команды, когда нужно предотвратить добавление новых команд через ;, &&, |. escapeshellarg предпочтительнее для безопасной передачи отдельных аргументов, так как полностью изолирует их в кавычках. В большинстве случаев escapeshellarg считается более безопасным выбором.
Использует передачу аргументов списком, что исключает необходимость экранирования через shell.
import subprocess
subprocess.run(['ls', '-la', 'my file.txt'])
При необходимости использования shell=True применяют shlex.quote():
import shlex
print(shlex.quote("echo 'test' && ls"))
'echo '\''test'\'' && ls'
Аналогично Python, рекомендуется передавать аргументы массивом:
const { spawn } = require('child_process');
const ls = spawn('ls', ['-la', 'my file.txt']);
Escapeshellcmd в MySQL
Для экранирования shell-команд не применяется, но есть функции для экранирования SQL-запросов (mysql_real_escape_string()), которые решают схожие задачи безопасности на другом уровне.
В отличие от PHP, современные практики в Python и Node.js предполагают минимальное использование shell, предпочитая прямое выполнение команд с раздельными аргументами, что фундаментально безопаснее.
Ошибочно экранировать части команды по отдельности:
// Неправильно!
$cmd = escapeshellcmd('ls -la') . ' ' . escapeshellcmd($_GET['dir']);
// Если $_GET['dir'] = "/tmp; cat /etc/passwd"
// Результат: ls\ -la /tmp\;\ cat\ /etc/passwd
// Команда 'cat' не выполнится, но структура нарушена
Правильный подход - экранировать всю команду целиком или использовать escapeshellarg для аргументов:
$cmd = 'ls -la ' . escapeshellarg($_GET['dir']);
escapeshellcmd не защищает от всех угроз и не должна быть единственной мерой безопасности:
// Функция не экранирует пробелы внутри аргументов
$cmd = escapeshellcmd('cat /etc/passwd');
exec($cmd); // Команда ВЫПОЛНИТСЯ!
Для таких случаев нужно валидировать или ограничивать входные данные.
// Может сломать пути на Windows
$path = 'C:\Program Files\app.exe';
echo escapeshellcmd($path);
C:Program Filesapp.exe // Обратные слеши удалены!
В Windows экранирование стало более безопасным: теперь экранируются символы ^, &, %, (, ), [, ], {, }, =, ;, !, +, ~, ,, ", ', <, >, | и пробел.
В PHP 5.4.0 и 5.4.1 функция экранировала символ #. Начиная с PHP 5.4.2, этот символ больше не экранируется, так как он не представляет опасности в контексте shell-команд.
Поведение функции различается на Unix-системах и Windows из-за различий в shell. В Unix экранируются #&;`|*?~<>^()[]{}$\'"\x0A\xFF, а в Windows - дополнительный набор символов, специфичный для cmd.exe.
$user = "O'Connor";
$file = "data; echo 'inject'";
$cmd = sprintf(
"convert %s -resize 800x600 %s",
escapeshellarg($user),
escapeshellarg($file)
);
echo escapeshellcmd($cmd);
convert 'O'\''Connor' -resize 800x600 'data; echo '\''inject'\'''
$pattern = ".php -exec rm {} \\;";
$safe_pattern = escapeshellcmd($pattern);
$command = "find /var/www -name '*$safe_pattern'";
echo $command;
find /var/www -name '*\\.php\ -exec\ rm\ \{\}\ \\\;'
$cmd = "dir C:\Users & echo Vulnerable";
echo escapeshellcmd($cmd);
dir C:Users ^& echo Vulnerable
function safe_shell_exec($base_cmd, $user_input) {
$full_cmd = $base_cmd . ' ' . escapeshellcmd($user_input);
// Дополнительная проверка на допустимые символы
if (preg_match('/[^a-zA-Z0-9_\-\.\/\s]/', $user_input)) {
return "Invalid characters detected";
}
return exec($full_cmd);
}
echo safe_shell_exec('ls -la', '/tmp; cat /etc/passwd');
// Выполнится: ls -la /tmp\;\ cat\ /etc/passwd
$cmd = "cat log.txt | grep 'error' > errors.txt";
echo escapeshellcmd($cmd);
cat log.txt \| grep 'error' \> errors.txt
Иногда нужно экранировать только определенные символы:
function escape_specific($cmd, $chars = ';&|') {
return preg_replace('/([' . preg_quote($chars, '/') . '])/', '\\\\$1', $cmd);
}
echo escape_specific("echo test; ls", ';');
echo test\; ls