Какая разница между escapeshellarg и escapeshellcmd?

PHP имеет две тесно связанные функции, escapeshellarg() и escapeshellcmd(). Кажется, что они делают подобные вещи, а именно помогают сделать строку более безопасной для использования в system()/exec()/etc.

Какой я должен использовать? Я просто хочу иметь возможность вводить пользовательский ввод и запускать на нем команду, а не все взорвать. Если PHP имел функцию exec-type, которая взяла массив строк (например, argv), который обходит оболочку, я бы использовал это. Подобно функции Python subprocess.call().

Ответ 1

От http://ie2.php.net/manual/en/function.escapeshellarg.php

escapeshellarg() добавляет одинарные кавычки вокруг строки и кавычки/экранирует любой существующие одинарные кавычки, позволяющие вам передать строку непосредственно в оболочку функции и ее следует рассматривать как один безопасный аргумент.

escapeshellarg, как указывает его имя, используется как передающий аргумент оболочки. Например, вы хотите перечислить текущий каталог,

$dir = ".";
system('ls '.escapeshellarg($dir));
escapeshellcmd('ls $dir');

Оба делают подобные вещи и просто зависят от того, как вы справляетесь с своей логикой, убедитесь, что вы нормализуете и проверяете свой ввод, прежде чем перейти непосредственно к этим методам для лучшей безопасности.

Ответ 2

Как правило, вы захотите использовать escapeshellarg, сделав один аргумент безопасности оболочки. Вот почему:

Предположим, вам нужно получить список файлов в каталоге. Вы получаете следующее:

$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 /`;

Поскольку указанный путь был использован unsanitised, любая команда может быть запущена. Мы можем использовать методы 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 делает строку безопасной для использования в качестве единственного аргумента для команды.

Ответ 3

Простое решение для определения разницы между любыми двумя аналогично звучащими функциями PHP состоит в том, чтобы написать в PHP краткую командную строку script, которая выводит все возможное пространство поиска и просто показывает различия (в этом случае сравнение 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";
    }
?>

Запуск выше в PHP 5.6 на выходах командной строки Windows:

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:  \ != \\

Запуск того же script под версиями 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:  ÿ !=

Основное отличие заключается в том, что PHP escapeshellcmd() под Windows префиксами символов с кареткой ^ вместо обратного слэша \. Необычность 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"'

PHP escapeshellarg() в Windows окружает строку символом двойной кавычки, в то время как Linux использует символ одной кавычки. PHP на Windows полностью заменяет внутренние двойные кавычки пробелами (что может быть проблемой в некоторых случаях) PHP на Linux немного отстает, чтобы избежать одиночных кавычек и обратных косых черт\экранировано\\в Windows. PHP escapeshellarg() в Windows также заменяет! И% символов пробелами. Все платформы заменяют \0 пробелами.

Обратите внимание, что поведение не обязательно согласовано между версиями PHP, и документация PHP не всегда отражает реальность. Написание быстрого script или чтение исходного кода PHP - это два способа выяснить, что происходит за кулисами.

Ответ 4

В документах PHP написано различие:

EscapeShellCmd

Следующим символам предшествует обратная косая черта: # &; `| *? ~ < > ^() [] {} $\, \ x0A и \xFF. 'и "экранируются только в том случае, если они не спарены. Windows, все эти символы плюс% заменяются пробелом.

escapeshellarg

добавляет одинарные кавычки вокруг строки и кавычки/экранирует любые существующие одинарные кавычки, позволяющие передавать строку непосредственно в оболочку и иметь его как единый безопасный аргумент.

Источник:

http://www.php.net/manual/en/function.escapeshellcmd.php http://www.php.net/manual/en/function.escapeshellarg.php