PHPでeval evilはいつですか?
私がphpで開発してきた何年にもわたって、eval()
を使用するのは悪いことだといつも聞いていました。
次のコードを考えると、2番目の(よりエレガントな)オプションを使用するのは理にかなっていますか?そうでない場合、なぜですか?
// $type is the result of an SQL statement
// e.g. SHOW COLUMNS FROM a_table LIKE 'a_column';
// hence you can be pretty sure about the consistency
// of your string
$type = "enum('a','b','c')";
// possibility one
$type_1 = preg_replace('#^enum\s*\(\s*\'|\'\s*\)\s*$#', '', $type);
$result = preg_split('#\'\s*,\s*\'#', $type_1);
// possibility two
eval('$result = '.preg_replace('#^enum#','array', $type).';');
eval()を純粋な悪と呼ぶことに注意します。動的評価は強力なツールであり、時には命を救うことができます。 eval()を使用すると、PHP(下記参照)の欠点を回避できます。
Eval()の主な問題は次のとおりです。
- 潜在的な安全でない入力。信頼できないパラメーターを渡すことは失敗する方法です。パラメーター(またはその一部)が完全に信頼されていることを確認することは、多くの場合簡単な作業ではありません。
- Trickiness。 eval()を使用すると、コードが賢くなり、従うのが難しくなります。ブライアン・カーニガンを引用すると、「デバッグは、最初にコードを書くよりも2倍難しい。したがって、コードをできる限り巧みに書くと、定義上、あなたはdebug it」
Eval()の実際の使用に関する主な問題は1つだけです。
- 十分な考慮なしにそれを使用する経験の浅い開発者。
経験則として、私はこれに従う傾向があります:
- Eval()が唯一の/正しいソリューションである場合があります。
- ほとんどの場合、他のことを試してください。
- 不明な場合は、2に進みます。
- それ以外の場合、非常に、非常に注意してください。
評価された文字列にユーザー入力が含まれる可能性がほんのわずかしかない場合、evalは悪です。ユーザーからのコンテンツなしでevalを実行する場合、安全である必要があります。
それにもかかわらず、evalを使用する前に少なくとも2回考える必要があります。これは非常に単純に見えますが、エラー処理(VBAssassinsのコメントを参照)、デバッグ可能性などを念頭に置いて、もはやそれほど単純ではありません。
経験則として:忘れてください。 evalが答えであるとき、あなたは間違った質問をしていることが適切です! ;-)
eval()は常に等しく悪です。
「eval()はいつ悪ではないのですか?」 eval()を使用することの欠点がいくつかのコンテキストで魔法のように消えることを暗示しているように見えるので、私の意見で尋ねるのは間違った質問です。
Eval()の使用は、コードの可読性、実行前にコードパス(およびその可能性のあるセキュリティの影響)を予測する能力、したがってコードをデバッグする能力を低下させるため、一般的に悪い考えです。 eval()を使用すると、評価されたコードとその周囲のコードが、PHP 5.5以降に統合されたZend Opcacheなどのオペコードキャッシュ、またはHHVMの1つ。
さらに、eval()を使用することが絶対に必要な状況はありません-PHPは、それなしで完全に機能するプログラミング言語です。
これらを実際に悪と見なすか、場合によってはeval()を使用して個人的に正当化できるかどうかはあなた次第です。一部の人にとっては、悪はそれを正当化するには大きすぎます。他の人にとっては、eval()は便利なショートカットです。
ただし、eval()を悪と見なすと、常に悪になります。状況に応じて魔法のように悪を失うことはありません。
この場合、ユーザーがテーブルに任意の列を作成できない限り、evalはおそらく十分に安全です。
しかし、実際にはこれ以上エレガントではありません。これは基本的にテキスト解析の問題であり、PHPのパーサーを処理するために乱用するのは少しハックのようです。言語機能を悪用したい場合は、JSONパーサーを悪用しないでください。少なくともJSONパーサーでは、コードインジェクションのすべての可能性はありません。
$json = str_replace(array(
'enum', '(', ')', "'"), array)
'', '[', ']', "'"), $type);
$result = json_decode($json);
正規表現はおそらく最も明白な方法です。単一の正規表現を使用して、この文字列からすべての値を抽出できます。
$extract_regex = '/
(?<=,|enum\() # Match strings that follow either a comma, or the string "enum("...
\' # ...then the opening quote mark...
(.*?) # ...and capture anything...
\' # ...up to the closing quote mark...
/x';
preg_match_all($extract_regex, $type, $matches);
$result = $matches[1];
Eval内で外部データ(ユーザー入力など)を使用している場合。
上記の例では、これは問題ではありません。
eval()
は遅いですが、悪とは言いません。
コードインジェクションにつながり、悪になる可能性があるのは、私たちがそれを悪用していることです。
簡単な例:
_$_GET = 'echo 5 + 5 * 2;';
eval($_GET); // 15
_
有害な例:
_$_GET = 'system("reboot");';
eval($_GET); // oops
_
eval()
を使用しないことをお勧めしますが、使用する場合は、すべての入力を検証/ホワイトリストに登録してください。
ここでコンテンツを露骨に盗みます:
評価はその性質上、常にセキュリティ上の懸念事項です。
Evalには、セキュリティ上の懸念の他に、非常に遅いという問題もあります。 PHP 4.3.10でのテストでは、通常のコードよりも10倍遅く、PHP 5.1 beta1。
個人的には、コードが何をしているのかコメントしていないので、コードはまだかなり悪いと思います。また、入力の妥当性をテストしていないため、非常に脆弱です。
また、evalの使用の95%(またはそれ以上)が積極的に危険であるため、他の場合に提供される可能性のある小さな潜在的な時間節約は、それを使用する悪い習慣にふける価値がないと感じています。加えて、evalの使用がなぜ良いのか、なぜ悪いのかを後で手先に説明しなければなりません。
そして、もちろん、PHPはPerlのように見えます;)
Eval()には2つの重要な問題があります(「インジェクション攻撃」シナリオとして):
1)害を引き起こす可能性がある2)単にクラッシュする可能性がある
そして、技術的よりも社会的なもの:
3)他の場所でショートカットとして不適切に使用したくなる
最初のケースでは、任意のコード実行のリスク(明らかに、既知の文字列を評価しているときではなく)を実行します。しかし、あなたの入力はあなたが思っているほど知られていないか、固定されていないかもしれません。
(この場合)より可能性が高いのは、単にクラッシュするだけで、文字列が不明瞭なエラーメッセージで終了することです。私見、すべてのコードは可能な限りきれいに失敗する必要があり、失敗すると例外がスローされます(最も扱いやすい形式のエラーとして)。
この例では、動作をコーディングするのではなく、偶然でコーディングすることをお勧めします。はい、SQL enumステートメント(およびそのフィールドのenumですか?-適切なバージョンのデータベースの適切なテーブルの適切なフィールドを呼び出しましたか?実際に答えましたか?)は、PHPの配列宣言構文のように見えます。しかし、あなたが本当にやりたいことは、入力から出力までの最短経路を見つけるのではなく、指定されたタスクに取り組むことです。
- 列挙型があることを確認します
- 内部リストを抽出する
- リスト値を解凍します
これはおおよその選択肢の1つですが、明確性と安全性のためにifの一部とコメントをラップします(たとえば、最初の一致が一致しない場合、例外をスローするか、null結果を設定します)。
エスケープされたカンマまたは引用符にはいくつかの問題が残っている可能性があり、おそらくデータをアンパックしてからクォートを解除する必要がありますが、少なくともデータをコードとしてではなくデータとして扱います。
Preg_versionでは、最悪の結果は$ result = nullになる可能性が高く、evalバージョンでは最悪の結果は不明ですが、少なくともクラッシュします。
また、あなたのコードを保守している人々にもいくらか配慮を払っています。
eval()は、何が起こるべきかをただ見たり知ったりするだけではなく、あなたの例はそれほど悪くはありませんが、他の場所ではそれは正しい悪夢でありえます。
eval()
は常に悪です。
- セキュリティ上の理由から
- パフォーマンス上の理由で
- 読みやすさ/再利用性の理由から
- IDE /ツールの理由
- デバッグのため
- 常により良い方法があります
evalは文字列をコードとして評価しますが、それに関する問題は、文字列が何らかの方法で「汚染された」場合、巨大なセキュリティ上の脅威をさらす可能性があることです。通常、問題はユーザー入力が文字列で評価される場合であり、多くの場合、ユーザーはeval内で実行されるコード(たとえば、phpまたはssi)を入力でき、phpスクリプトと同じ権限で実行され、サーバーへの情報/アクセスに使用されます。 evalに渡す前に、ユーザー入力が適切に消去されていることを確認するのは非常に難しい場合があります。他にも問題があります...そのうちのいくつかは議論の余地があります
PHPは、明示的な評価を行う代わりに、call_user_funcを介して実行できるようにコードを記述することをお勧めします。
関数ではなくeval()を悪にするのは悪いプログラミングです。複数のサイトでの動的プログラミングでは回避できないため、時々使用します。 PHPを1つのサイトで解析することはできません。必要なものを受け取れないためです。結果を受け取るだけです!eval()としての関数が存在することを嬉しく思います。ユーザー入力?悪いプログラマーだけがハッカーに接続されますが、心配する必要はありません。
私はあなたがすぐに深刻な問題を抱えると予測しています...
正直なところ、PHPなどのインタープリター言語では、evalなどの法外な関数を使用することは絶対にありません。他のより安全な方法では実行できなかったevalがプログラム機能を実行するのを見たことはありません...
Evalはすべての悪の根源であり、ユーザー入力のテストが役立つと考えるすべての人々にとって、私は心から同意します。考え直してください。ユーザー入力はさまざまな形で発生する可能性がありますが、ハッカーはその機能を悪用しているので、気にしませんでした。私の意見では、評価は完全に避けてください。
私は、自分の創造性を超えた評価関数を悪用する巧妙な例を見てきました。セキュリティの観点からは、すべてのコストを避けてください。少なくとも、「与えられた」ではなくPHP構成のオプションであるように要求することもできます。
関数ではなくeval()を悪にするのは悪いプログラミングです。複数のサイトでの動的プログラミングでは回避できないため、時々使用します。 PHPは、私が欲しいものを受け取れないので、1つのサイトで解析されます。結果を受け取るだけです!eval()が存在するので、関数が嬉しいです。ユーザー入力?悪いプログラマーだけがハッカーに夢中になりますが、心配する必要はありません。
eval
が悪であるもう1つの理由は、PHP eAccelertorやACPのようなバイトコードキャッシュによってキャッシュできなかったということです。
私はeval()を頻繁に使用していましたが、トリックを行うためにevalを使用する必要がないほとんどの場合が見つかりました。さて、PHPにはcall_user_func()とcall_user_func_array()があります。任意のメソッドを静的および動的に呼び出すのに十分です。
静的呼び出しを実行するには、コールバックをarray( 'class_name'、 'method_name')として、または 'class_name :: method_name'のような単純な文字列として作成します。動的呼び出しを実行するには、array($ object、 'method')スタイルのコールバックを使用します。
Eval()の賢明な使用法は、カスタムコンパイラを記述することだけです。作成しましたが、evalはまだ悪です。デバッグが非常に難しいからです。最悪の事態は、評価されたコードの致命的なエラーが、それを呼び出したコードをクラッシュさせることです。少なくとも構文をチェックするためにParsekit PECL拡張モジュールを使用しましたが、それでも喜びはありません。未知のクラスとアプリ全体のクラッシュを参照してみてください。
以下は、evalを使用せずにデータベースからプルしたコードPHPを実行するためのソリューションです。スコープ内のすべての関数と例外を許可します。
$rowId=1; //database row id
$code="echo 'hello'; echo '\nThis is a test\n'; echo date(\"Y-m-d\");"; //php code pulled from database
$func="func{$rowId}";
file_put_contents('/tmp/tempFunction.php',"<?php\nfunction $func() {\n global \$rowId;\n$code\n}\n".chr(63).">");
include '/tmp/tempFunction.php';
call_user_func($func);
unlink ('/tmp/tempFunction.php');
基本的に、テキストファイルに含まれるコードを使用して一意の関数を作成し、ファイルを含め、関数を呼び出し、処理が完了するとファイルを削除します。私はこれを使用して、データベースの取り込み/同期を毎日実行し、すべてのステップで処理するために一意のコードが必要になります。これは私が直面していたすべての問題を解決しました。
セキュリティ上の問題を除いて、eval()はコンパイル、最適化、またはオペコードキャッシュできないため、通常のphpコードよりも常に遅くなります-way slower-。したがって、evalを使用するのは非パフォーマンスですが、evalを使用しても悪ではありません。 (goto
は悪で、eval
は悪い練習/臭いコード//いだけです)