web-dev-qa-db-ja.com

escapeshellargとescapeshellcmdの違いは何ですか?

PHPには2つの密接に関連する関数 escapeshellarg() および escapeshellcmd() があります。どちらも同じようなことをしているようです。つまり、文字列をsystem()/exec()/etcでより安全に使用できるようにするのに役立ちます。

どちらを使用すればよいですか?一部のユーザー入力を取得してコマンドを実行できるようにしたいのですが、すべてが爆発することはありません。 PHPに、シェルをバイパスする文字列の配列(argvなど)を取るexec-type-functionがあった場合、それを使用します。Pythonの subprocess.call() 関数。

37
Rory

http://ie2.php.net/manual/en/function.escapeshellarg.php から

escapeshellarg()は、文字列の周りに単一引用符を追加し、既存の単一引用符を引用/エスケープして、文字列を直接Shell関数に渡し、単一の安全な引数として処理させることができます。

escapeshellargは、その名前が示すように、シェル引数を渡すために使用されます。たとえば、現在のディレクトリを一覧表示したい場合、

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

どちらも同様のことを行い、ロジックの処理方法に依存します。セキュリティを向上させるために、これらのメソッドに直接渡す前に入力を正規化および検証してください。

4
Jay Zeng

一般的に、シェルコマンドへの単一の引数を安全にする 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 /`;

指定されたパスは無害に使用されたため、すべてのコマンドが実行される可能性があります。 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は文字列が1つのコマンドのみであることを確認し、escapeshellargは文字列をコマンドへの単一の引数として安全に使用できるようにします。

95
Adam

2つの類似したサウンドの違いを判断するための簡単な解決策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";
    }
?>

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

PHP 5.5で同じスクリプトを実行する:

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の両方の奇妙な点は、両方の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は単一引用符 'を使用します。PHPは内部の二重引用符をスペースで完全に置き換えます(これはPHPは一重引用符をエスケープするのに少し邪魔になり、円記号\はWindowsの\\エスケープされます。PHP = Windowsのescapeshellarg()も!および%文字をスペースに置き換えますすべてのプラットフォームが\ 0をスペースに置き換えます。

PHPバージョンとPHPドキュメントは必ずしも現実を反映しているわけではありません。簡単なスクリプトの記述または= PHPは、舞台裏で何が起こっているのかを理解する2つの方法です。

4
CubicleSoft

PHP docsは違いを詳しく説明しています:

escapeshellcmd

次の文字の前にはバックスラッシュがあります:#&; `| *?〜<> ^()[] {} $ \、\ x0Aおよび\ xFF。 'と "は、ペアになっていない場合にのみエスケープされます。Windowsでは、これらすべての文字と%が代わりにスペースに置き換えられます。

escapeshellarg

文字列を一重引用符で囲み、既存の単一引用符を引用/エスケープして、文字列を直接Shell関数に渡し、それを単一の安全な引数として処理させることができます。

ソース:

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

4
mph