eval 関数はコードを動的に生成するための強力で簡単な方法ですが、注意点は何ですか?
25 に答える
evalを不適切に使用すると、コードがインジェクション攻撃にさらされる
デバッグはより困難になる可能性があります (行番号がないなど)。
eval されたコードの実行が遅くなる (eval されたコードをコンパイル/キャッシュする機会がない)
編集: @Jeff Walden がコメントで指摘しているように、#3 は 2008 年よりも今日では当てはまりません。より可能性の高いシナリオは、毎回わずかな変更が加えられたスクリプトを評価しているため、キャッシュできなかったということです。一部の評価済みコードの実行速度が遅いとだけ言っておきましょう。
eval は常に悪であるとは限りません。それが完全に適切な場合があります。
しかし、eval は現在も歴史的にも、自分が何をしているのかを知らない人々によって大量に過剰に使用されています。残念ながら、これには JavaScript チュートリアルを書いている人も含まれており、場合によっては、実際にセキュリティに影響を与える可能性があります。または、より多くの場合、単純なバグです。したがって、eval にクエスチョン マークを付けるためにできることは多ければ多いほどよいのです。eval を使用するときはいつでも、自分が行っていることを健全性チェックする必要があります。
あまりにも典型的な例を挙げると、変数 'potato' に格納された ID を持つ要素の色を設定するには:
eval('document.' + potato + '.style.color = "red"');
上記の種類のコードの作成者が、JavaScript オブジェクトがどのように機能するかの基本についての手がかりを持っていれば、リテラルのドット名の代わりに角かっこを使用して、eval の必要性をなくすことができることに気付いたはずです。
document[potato].style.color = 'red';
...これははるかに読みやすく、潜在的にバグが少ないです。
(しかし、彼らが何をしているかを/本当に/知っている誰かが言うだろう:
document.getElementById(potato).style.color = 'red';
これは、ドキュメント オブジェクトから直接 DOM 要素にアクセスするという危険な古いトリックよりも信頼性が高くなります。)
文字列から任意の JavaScript 関数を実行できるためだと思います。これを使用すると、不正なコードをアプリケーションに簡単に挿入できます。
2つのポイントが思い浮かびます。
セキュリティ(ただし、自分で評価する文字列を生成する限り、これは問題にならない可能性があります)
パフォーマンス:実行するコードが不明になるまで、最適化することはできません。(javascriptとパフォーマンスについて、確かにSteve Yeggeのプレゼンテーション)
It's generally only an issue if you're passing eval user input.
ユーザー入力を eval() に渡すことはセキュリティ リスクですが、eval() を呼び出すたびに JavaScript インタープリターの新しいインスタンスが作成されます。これはリソースを大量に消費する可能性があります。
主に、保守とデバッグがはるかに困難です。のようなものgoto
です。使用することはできますが、問題を見つけるのが難しくなり、後で変更が必要になる可能性がある人にとっては難しくなります。
心に留めておくべきことの 1 つは、eval() を使用して、そうでなければ制限された環境でコードを実行できることです。特定の JavaScript 関数をブロックするソーシャル ネットワーキング サイトは、eval ブロックでそれらを分割することでだまされることがあります。
eval('al' + 'er' + 't(\'' + 'hi there!' + '\')');
そのため、他の方法では許可されていない JavaScript コード ( Myspace、私はあなたを見ています...) を実行しようとしている場合は、 eval() が便利なトリックになる可能性があります。
ただし、上記のすべての理由から、完全に制御できる独自のコードには使用しないでください。これは必要ではなく、「トリッキーな JavaScript ハック」の棚に追いやった方がよいでしょう。
eval() を (cgi または入力によって) 動的コンテンツにしない限り、ページ内の他のすべての JavaScript と同じくらい安全で堅固です。
残りの回答と同様に、評価ステートメントで高度な最小化を行うことはできないと思います。
これはセキュリティリスクの可能性があり、実行範囲が異なり、コードを実行するためのまったく新しいスクリプト環境を作成するため、非常に非効率的です。詳細については、 evalを参照してください。
ただし、これは非常に便利であり、適度に使用すると、多くの優れた機能を追加できます。
評価されるコードが信頼できるソース (通常は独自のアプリケーション) からのものであることが 100% 確実でない限り、システムをクロスサイト スクリプティング攻撃にさらす確実な方法です。
使用しているコンテキストがわかっている場合は、必ずしもそれほど悪いわけではありません。
アプリケーションがXMLHttpRequesteval()
から独自のサイトに返された JSON からオブジェクトを作成するために使用し、信頼できるサーバー側コードによって作成された場合、おそらく問題はありません。
いずれにせよ、信頼できないクライアント側の JavaScript コードでは、それほど多くのことはできません。あなたが実行eval()
しているものが合理的なソースからのものであれば、問題ありません。
セキュリティに対する信頼度が大幅に低下します。
ユーザーにいくつかの論理関数を入力して AND を評価させたい場合は、JavaScript の eval 関数が最適です。eval(uate) string1 === string2
となどの 2 つの文字列を受け入れることができます。
次世代のブラウザにはJavaScriptコンパイラのフレーバーが搭載されているため、これはさらに問題になる可能性があります。Evalを介して実行されたコードは、これらの新しいブラウザーに対して、JavaScriptの他の部分と同様に機能しない場合があります。誰かがプロファイリングを行う必要があります。
それは必ずしも悪い考えではありません。コード生成を例にとってみましょう。私は最近、 virtual-domとhandlebarsの間のギャップを埋めるHyperbarsというライブラリを作成しました。これは、ハンドルバー テンプレートを解析し、後で virtual-dom によって使用されるハイパースクリプトに変換することによって行われます。ハイパースクリプトは最初に文字列として生成され、それを返す前に実行可能コードに変換されます。私は、この特定の状況で、悪とは正反対のことを発見しました。eval()
eval()
基本的には
<div>
{{#each names}}
<span>{{this}}</span>
{{/each}}
</div>
これに
(function (state) {
var Runtime = Hyperbars.Runtime;
var context = state;
return h('div', {}, [Runtime.each(context['names'], context, function (context, parent, options) {
return [h('span', {}, [options['@index'], context])]
})])
}.bind({}))
このような状況では、 のパフォーマンスはeval()
問題になりません。生成された文字列を解釈する必要があるのは 1 回だけで、実行可能な出力を何度も再利用できるからです。
興味があれば、コード生成がどのように行われたかを確認できます。
これは、eval とそれが悪ではないことについて話している良い記事の 1 つです 。
私は、あなたが尽力してどこでも eval() を使い始めるべきだと言っているわけではありません。実際、eval() を実行する適切な使用例はほとんどありません。コードの明瞭さ、デバッグ可能性、そして確かにパフォーマンスに関して見逃すべきではない懸念があります。しかし、eval() が理にかなっている場合には、それを使用することを恐れてはなりません。最初は使用しないようにしてください。ただし、eval() を適切に使用すると、コードが脆弱になったり、安全性が低下したりするなどと考えさせられないようにしてください。
JavaScript エンジンには、コンパイル フェーズ中に実行するパフォーマンスの最適化が多数あります。これらのいくつかは、要約すると、コードを字句解析するときにコードを本質的に静的に分析し、すべての変数と関数の宣言がどこにあるかを事前に決定できるため、実行中に識別子を解決する労力が少なくて済みます。
しかし、エンジンがコード内に eval(..) を見つけた場合、eval(..) にどのコードを渡すことができるかを字句解析時に正確に知ることができないため、基本的に、識別子の場所のすべての認識が無効であると想定する必要があります。レキシカル スコープ、または参照する新しいレキシカル スコープを作成するために渡すことができるオブジェクトの内容を変更します。
言い換えれば、悲観的な意味で、 eval(..) が存在する場合、これらの最適化のほとんどは無意味であるため、最適化をまったく実行しません。
これですべてが説明されます。
参照 :
https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/ch2.md#eval
https://github.com/getify/You-Dont-Know-JS/blob/master/scope%20&%20closures/ch2.md#performance
ユーザーが送信したコードを実行する場合に起こりうるセキュリティ上の問題に加えて、ほとんどの場合、コードを実行するたびにコードを再解析する必要のないより良い方法があります。匿名関数またはオブジェクト プロパティは、eval のほとんどの使用を置き換えることができ、はるかに安全で高速です。