PHPのeval悪はいつですか?


84

私が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).';');

2
evalは常に悪であり、特にPHPが無名関数を導入して以来、コードを記述するためのより良い方法が常にあります。この場合、私は使用します$result = array(); preg_replace_callback('#^enum\s*\(\s*\'|\'\s*\)\s*$#', function($m) use($result) { $result[] = $m[1]; }, $type);
Geoffrey

正直なところ、phpの主な問題は言語そのものではなく、それを使用する人々だと思います。この質問に対する3つの正解(thomasrutter、braincracking、およびmine)はすべて、誰も反対することなく反対票を投じました。一方、ある回答は、例や説明なしに「eval()が唯一の/正しい解決策である場合がある」と主張し、それに賛成票を投じます...
FrancoisBourgeois18年

回答:


133

私はeval()を純粋な悪と呼ぶことに注意するでしょう。動的評価は強力なツールであり、命を救うこともあります。eval()を使用すると、PHPの欠点を回避できます(以下を参照)。

eval()の主な問題は次のとおりです。

  • 安全でない可能性のある入力。信頼できないパラメータを渡すことは失敗する方法です。多くの場合、パラメーター(またはその一部)が完全に信頼されていることを確認するのは簡単な作業ではありません。
  • トリッキー。eval()を使用すると、コードが巧妙になるため、追跡が難しくなります。Brian Kernighanの言葉を引用すると、「デバッグは、そもそもコードを書くよりも2倍難しい。したがって、コードをできるだけ巧妙に書くと、定義上、デバッグするほど賢くはない

eval()の実際の使用に関する主な問題は、次の1つだけです。

  • 十分な考慮なしにそれを使用する経験の浅い開発者。

経験則として、私はこれに従う傾向があります:

  1. eval()が唯一の/正しい解決策である場合があります。
  2. ほとんどの場合、他のことを試してみてください。
  3. 不明な場合は、2に進んでください。
  4. それ以外の場合は、非常に注意してください。

4
これは、eval()を回避するためにもリファクタリングできます。特に、$ classNameがホワイトリストに登録されている場合は、eval()の使用を楽しませるためにホワイトリストに登録する必要があります。ただし、5.3の時点では、$ foo :: bar()は有効です。
rojoca 2009年

@rojoca:PHP 5.2でeval()なしでそれを行う方法の例を教えてください。
のMichałRudnicki

30
evalとしてカウントされるかどうかはわかりませんが、$ result = call_user_func(array( 'Foo'、 'bar')); チャームのように機能します。
Ionuț G. Stan

3
トリッキーさの良い点-あなたの(単純な)例では、変数は「どこからともなく」飛び出します。コードがもう少し複雑になった場合は、コードを見てその変数を追跡しようとする次の人に幸運を祈ります(そこで、私が得たのは頭痛とこのお粗末なTシャツだけでした)。
Piskvorは、2010

安全でない入力は避けられます
thepowerlies

40

評価された文字列にユーザー入力が含まれる可能性がわずかしかない場合、evalは悪です。ユーザーからのコンテンツなしでevalを実行する場合は、安全である必要があります。

それでも、evalを使用する前に、少なくとも2回考える必要があります。一見シンプルに見えますが、エラー処理(VBAssassinsのコメントを参照)、デバッグ可能性などを念頭に置いて、もはやそれほどシンプルではありません。

したがって、経験則として、それについては忘れてください。evalが答えである場合、あなたはおそらく間違った質問をしているのです!;-)


6
時々evalが答えです。私たちはオンラインゲームアプリケーションに取り組んでおり、エンティティ間の関係が非常に複雑であるため、そこで評価を回避することは非常に困難です...しかし、90%のケースでIMHOは評価が答えではありません。
ジェット

19
evalだけが答えである状況を見て本当に興味があります。
Ionuț G. Stan

2
@Ionut G. Stanデータベースに保存されたオブジェクト/エンティティのカスタムトリガー?
黒木風

8
これらのいずれかがeval()の正当な使用法であることを私は実際には購入しません。いずれの場合も、eval()を使用せずに同じことを行うことは少なくとも可能です。PHPがeval()なしで機能しなくなるわけではありません。確かに、eval()はこのような場合のショートカットですが、それでもコードパスをたどるのが少し難しくなり、問題のデバッグが少し難しくなります。それが私の意見です。
thomasrutter 2009年

4
@Christian:あなたが最初の(使用例を記述する必要がありますpastebin.comを、そしてここにリンクを貼り付け)、あなたはの使用を避けることだと思うところeval()不可能である、これはより良いアプローチです。多くの人eval()が、場合によっては避けられないと言いますが、私たちが主張できる具体的な例については言及していません。このように、この議論は意味がありません。ですから、eval()避けられないと言う人は、まずそれを証明してください!
sk8erPeter 2013年

18

eval()は常に同じように悪です。

「eval()が悪ではないのはいつですか?」eval()を使用することの欠点がいくつかのコンテキストで魔法のように消えることを意味しているように思われるので、私の意見では間違った質問です。

eval()を使用すると、コードの可読性、実行前にコードパス(およびそのセキュリティへの影響)を予測する機能、したがってコードをデバッグする機能が低下するため、一般的にはお勧めできません。eval()を使用すると、評価されたコードとその周辺のコードが、PHP5.5以降に統合されたZendOpcacheなどのオペコードキャッシュや、HHVMなどのJITコンパイラによって最適化されないようにすることもできます。

さらに、eval()を使用することが絶対に必要な状況はありません。PHPは、eval()がなくても完全に機能するプログラミング言語です。

これらを実際に悪と見なすかどうか、または場合によってはeval()を使用して個人的に正当化できるかどうかはあなた次第です。一部の人にとっては、悪はそれを正当化するには大きすぎるし、他の人にとっては、eval()は便利なショートカットです。

ただし、eval()を悪と見なすと、常に悪になります。文脈によっては魔法のように悪を失うことはありません。


1
あなたはプログラマーの地獄を作ります。
クリスチャン

1
「さらに、eval()を使用することが絶対に必要な状況はありません」-PHPのドキュメントに示されている例に従って、データベースに保存したコードを実行したい場合はどうでしょうか。
ヤリン2011年

4
それに対して、PHPコードをデータベースに保存することも同様に悪であり、同様に不必要であると言えます。達成したいことは何でも、PHPをデータベースに保存したり、eval()を使用したりする以外の方法があります。必要に応じて実行し、それが便利なショートカットであるが、eval()を悪として扱う場合は、データベースへのPHPの格納も悪として扱う必要があります。
thomasrutter 2011年

12
別の攻撃ベクトルを追加します。SQLインジェクションは、Webサーバー上で任意のPHPコードを実行することもできます。eval()を使用する必要があります。バイトコードキャッシュでは最適化できません。これは、コードとデータを分離するという原則に違反しています。アプリケーションの移植性が低下する可能性があります。PHPを更新/修正するには、データベースも更新する必要があります。
thomasrutter 2011年

3
evalを使用することによってのみ何かが達成できるのであれば、それを行うことを再考するのは良いことかもしれません。実行時にクラスを動的に作成することは悪い考えのようです。コード生成を採用して、それらを定義するコードを事前に生成してみませんか?
thomasrutter

15

この場合、ユーザーがテーブルに任意の列を作成することが不可能である限り、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];

1
これ自体は質問に答えませんでしたが、これは質問に対する非常に良い答えです。enum()を解析するための最良の方法は何でしょうか…ありがとう;)
Pierre Spring

13

eval() 遅いですが、私はそれを悪とは呼びません。

コードインジェクションにつながり、悪につながる可能性があるのは、私たちがそれを悪用することです

簡単な例:

$_GET = 'echo 5 + 5 * 2;';
eval($_GET); // 15

有害な例:

$_GET = 'system("reboot");';
eval($_GET); // oops

使用しないことをお勧めしますが、使用eval()する場合は、すべての入力を検証/ホワイトリストに登録してください。


12

eval内で外部データ(ユーザー入力など)を使用している場合。

上記の例では、これは問題ではありません。


7

ここで露骨にコンテンツを盗みます:

  1. その性質上、評価は常にセキュリティ上の懸念事項になります。

  2. セキュリティ上の懸念に加えて、evalには信じられないほど遅いという問題もあります。PHP 4.3.10での私のテストでは、通常のコードよりも10倍遅く、PHP 5.1beta1では28倍遅くなっています。

blog.joshuaeichorn.com:using-eval-in-php


5

eval()、常に悪。

  • セキュリティ上の理由から
  • パフォーマンス上の理由から
  • 読みやすさ/再利用性の理由から
  • IDE /ツールの理由で
  • デバッグ上の理由
  • 常により良い方法があります

@bracketworks:しかし、あなたは間違っています-これらの不法な問題はすべて、状況によっては魔法のように消えることはありません。なぜ彼らはすべきですか?パフォーマンスを例にとってみましょう。インタプリタが非常に遅い関数eval()を、神秘的な「悪ではない」方法で使用したという理由だけで、速度を上げて実行すると本当に思いますか?または、そのステップバイステップのデバッグは、幸運な日にeval句内で機能しますか?
フランソワブルジョワ2013年

3
率直に言って、@MichałRudnickiの答えが最も良いと思います。私が(自分自身に)質問をするときはいつでも、私を誤解しないでください、そして答えはeval()、私が間違った質問をしたとすぐに思います。それが「正しい」答えになることはめったにありませんが、一言で言えば、それは常に悪であり、単に間違っています。
ダンラグ2013年

コードをデータベースに保存したい場合は?どうすれば実行できますか?
Konstantin XFlash Stratigenas 2017

@KonstantinXFlashStratigenas実行可能コードをデータベースに保存する理由を自問する必要があります。データベースは、コードではなく、コードから得られたデータ用です。少なくとも、攻撃ベクトルを増やしています。
ジャック

4

また、コードを保守している人にも配慮します。

eval()は、何が起こるかを見て知るのは簡単ではありません。あなたの例はそれほど悪くはありませんが、他の場所では、それは正しい悪夢になる可能性があります。


4

個人的には、コードが何をしているのかコメントしていないので、コードはまだかなり邪悪だと思います。また、入力の有効性をテストしていないため、非常に壊れやすくなっています。

また、evalの使用の95%(またはそれ以上)は積極的に危険であるため、他の場合に提供される可能性のあるわずかな時間の節約は、それを使用する悪い習慣にふける価値がないと感じています。さらに、後で、評価の使用が良い理由と悪い理由をミニオンに説明する必要があります。

そしてもちろん、PHPはPerlのように見えます;)

eval()には2つの重要な問題があります(「インジェクション攻撃」シナリオとして)。

1)害を及ぼす可能性があります2)単にクラッシュする可能性があります

そして、技術的というよりも社会的なもの:

3)他の場所へのショートカットとして不適切に使用するように人々を誘惑します

最初のケースでは、任意のコードが実行されるリスクがあります(明らかに、既知の文字列を評価しているときではありません)。ただし、入力は、思ったほど知られていないか、固定されていない可能性があります。

より可能性が高いのは(この場合)クラッシュするだけで、文字列は不当にあいまいなエラーメッセージで終了します。私見ですが、すべてのコードは可能な限りきちんと失敗し、失敗すると例外がスローされます(最も扱いやすい形式のエラーとして)。

この例では、動作に合わせてコーディングするのではなく、偶然にコーディングしていることをお勧めします。はい、SQL列挙型ステートメント(そして、そのフィールドの列挙型は確かですか?-データベースの適切なバージョンの適切なテーブルの適切なフィールドを呼び出しましたか?実際に答えましたか?)は、PHPの配列宣言構文のように見えます。しかし、あなたが本当にやりたいことは、入力から出力への最短パスを見つけることではなく、指定されたタスクに取り組むことです。

  • 列挙型があることを確認します
  • 内部リストを抽出する
  • リスト値を解凍します

これはおおよそあなたのオプションの1つですが、わかりやすく安全にするために、ifとコメントをいくつかラップします(たとえば、最初の一致が一致しない場合は、例外をスローするか、nullの結果を設定します)。

エスケープされたコンマまたは引用符にはまだいくつかの問題が考えられます。おそらくデータを解凍してから引用符を外す必要がありますが、少なくともデータをコードではなくデータとして扱います。

preg_versionを使用すると、最悪の結果は$ result = nullになる可能性が高く、evalバージョンを使用すると、最悪の結果は不明ですが、少なくともクラッシュします。


3

evalは文字列をコードとして評価します。その問題は、文字列が何らかの形で「汚染」されていると、巨大なセキュリティの脅威にさらされる可能性があることです。通常、問題は、ユーザー入力が文字列で評価される場合です。多くの場合、ユーザーはコード(phpやssiなど)を入力してeval内で実行でき、phpスクリプトと同じ権限で実行できます。サーバーへの情報/アクセスを取得するために使用されます。evalに渡す前に、ユーザー入力が適切にクリーンアップされていることを確認するのは非常に難しい場合があります。他にも問題があります...そのうちのいくつかは議論の余地があります


3

PHPは、明示的な評価を行う代わりに、call_user_funcを介して実行できるようにコードを記述することをお勧めします。


2

もう1つの理由evalは、eAccelertorやACPなどのPHPバイトコードキャッシュでキャッシュできなかったことです。


2

関数ではなく、eval()を悪にするのは悪いプログラミングです。複数のサイトでの動的計画法では回避できないため、時々使用します。欲しいものが届かないので、1つのサイトでPHPを解析することはできません。結果が出るだけ!eval()が存在するので、私の生活がずっと楽になるので、私はこの関数に満足しています。ユーザー入力?悪いプログラマーだけがハッカーに夢中になります。私はそれについて心配しません。


2

関数ではなく、eval()を悪にするのは悪いプログラミングです。複数のサイトでの動的計画法では回避できないため、時々使用します。欲しいものが届かないので、1つのサイトでPHPを解析することはできません。結果が出るだけ!eval()が存在するので、私の生活がずっと楽になるので、私はこの関数に満足しています。ユーザー入力?悪いプログラマーだけがハッカーに夢中になります。私はそれについて心配しません。

私はあなたがすぐに深刻な問題を抱えることになると予測しています...

正直なところ、PHPなどのインタープリター言語では、evalなどの法外な関数を適切に使用することはできません。他のより安全な方法では実行できなかったevalがプログラム機能を実行するのを見たことがありません...

Evalはすべての悪の根源であり、ユーザー入力のテストが役立つと考えるすべての人々に心から同意します。よく考えてみてください。ユーザー入力にはさまざまな形式があります。私たちが話すように、ハッカーはあなたが十分に気にしていない機能を悪用しています。私の意見では、evalは完全に避けてください。

私は自分の創造性を超えた評価機能を悪用するために細工された例を見てきました。セキュリティの観点から、絶対に避けてください。私は、「与えられた」ではなく、少なくともPHP構成のオプションであるように要求することさえします。


「機能Xに関してコードを慎重に保護しようとしても、スマートハッカーは常に一歩先を行く...」という理由は、X ==だけでなく、他のどの手法にも当てはまりevalます。したがって、どの機能でもX:Xはすべての評価の根源です...えーと...悪なので、プログラミングを完全に諦めましょう(したがって、これらの愚かなハッカーの下からカーペットを引っ張って、最終的には彼らを裏切ります)。
Szを。

「evalのような法外な関数の良い使い方は絶対にありません」->「絶対に」のような言葉を使うことができる人は誰でも、プログラミングに不慣れであるか、悪いプログラマーです。
unity100 2016

2

これは、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');

基本的には、テキストファイルに含まれているコードを使用して一意の関数を作成し、ファイルを含め、関数を呼び出し、終了時にファイルを削除します。私はこれを使用して、毎日のデータベースの取り込み/同期を実行しています。この場合、すべてのステップで、処理するために一意のコードを処理する必要があります。これで私が直面していたすべての問題が解決しました。


これは回答への応答のように見えます。できればそのまま投稿してください。
rfornal 2015

1

以前はeval()をよく使用していましたが、トリックを行うためにevalを使用する必要がない場合がほとんどでした。さて、PHPにはcall_user_func()とcall_user_func_array()があります。任意のメソッドを静的および動的に呼び出すだけで十分です。

静的呼び出しを実行するには、コールバックをarray( 'class_name'、 'method_name')として、または 'class_name :: method_name'のような単純な文字列として作成します。動的呼び出しを実行するには、array($ object、 'method')スタイルのコールバックを使用します。

eval()の唯一の賢明な使用法は、カスタムコンパイラを作成することです。私は1つ作成しましたが、デバッグが非常に難しいため、evalは依然として悪です。最悪のことは、評価されたコードの致命的なエラーが、それを呼び出したコードをクラッシュさせることです。少なくとも構文をチェックするためにParsekitPECL拡張機能を使用しましたが、それでも喜びはありません。不明なクラスを参照すると、アプリ全体がクラッシュします。


0

セキュリティ上の問題以外には、evalの()は、コンパイルの最適化やオペコードは、このように、それはオールウェイズ遅くなり、キャッシュすることはできません-遅く道-通常のPHPコードより。したがって、evalを使用することはパフォーマンスに影響しませんが、それが悪になることはありません。(goto悪でevalあり、悪い練習/臭いコード/醜いだけです)


eval悪の評価はgoto?よりも低くなります。反対の日ですか?
JLRishe 2014年

goto5.3で実際に実装したphp開発者に恨みを抱いているだけです。
Aron Cederholm 2014年

1
gotoいや、-fobiansへ:ツールがあり、それらを使用する(または使用しない)職人がいます。間違いを犯したときにプロを馬鹿のように見せかけるのは決してツールではなく、適切なツールを(適切に)使用できないことです。しかし、それは、常に...非難するツール
Szを。

0

ほとんどの人は、ユーザー入力を処理するときに危険である可能性があるという事実を指摘します(これは処理可能です)。

私にとって最悪の部分は、コードの保守性が低下することです。

  • デバッグが難しい
  • 更新が難しい
  • ツールとヘルパー(IDEなど)の使用を制限します
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.