web-dev-qa-db-ja.com

JavaScriptのeval関数を使うのはなぜ悪い考えですか?

Eval関数は、動的にコードを生成するための強力で簡単な方法です。

503
Brian Singh
  1. evalを不適切に使用すると、コードがインジェクション攻撃を受ける可能性があります。

  2. デバッグはもっと難しくなります(行番号なしなど)。

  3. 評価されたコードは実行が遅くなります(評価されたコードをコンパイルまたはキャッシュする機会はありません)。

編集:@Jeff Waldenがコメントで指摘しているように、今日#3は2008年よりも真実ではありません。より可能性の高いシナリオは、毎回わずかな変更を加えたスクリプトを評価しているため、キャッシュできない可能性があることです。評価されたコードの実行速度が遅いということだけを言いましょう。

369
Prestaul

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要素にアクセスするという古くからあるトリックよりも信頼性が高くなります。)

343
bobince

文字列から任意のJavaScript関数を実行できるからです。これを使用すると、悪意のあるコードをアプリケーションに挿入しやすくなります。

36
kemiller2002

2つの点が思い浮かびます。

  1. セキュリティ(ただし、自分で評価する文字列を生成する限り、これは問題にならない場合があります)

  2. 性能:実行されるコードが未知になるまで、それは最適化されることができません。 (JavaScriptとパフォーマンスについては、確かに Steve Yeggeのプレゼンテーション

26
xtofl

ユーザー入力をeval()に渡すことはセキュリティ上のリスクですが、eval()を呼び出すたびにJavaScriptインタプリタの新しいインスタンスが作成されます。これはリソースを占有する可能性があります。

20
Andrew Hedges

あなたがevalユーザー入力を渡している場合、それは一般的に唯一の問題です。

17
Mark Biek

主に、保守やデバッグがずっと難しいです。それはgotoのようなものです。あなたはそれを使うことができますが、それは問題を見つけることを難しくし、後で変更を加える必要があるかもしれない人々をより困難にします。

15
Brian

覚えておくべき1つのことは、他の制限された環境でコードを実行するのにしばしばeval()を使うことができるということです - 特定のJavaScript関数をブロックするソーシャルネットワーキングサイトは時々evalブロックに分割することによってだまされる -

eval('al' + 'er' + 't(\'' + 'hi there!' + '\')');

そうでなければ許可されていない可能性がありますいくつかのJavaScriptコードを実行しようとしているのであれば( Myspace 、私はあなたを見ている...) eval()は便利なトリックです。

ただし、上記のすべての理由から、完全に制御できる独自のコードには使用しないでください。必要ではなく、「トリッキーなJavaScriptハック」シェルフに追いやられることをお勧めします。

13
matt lohkamp

Eval()を動的コンテンツ(cgiまたはinputを通じて)にしない限り、それはあなたのページの他のすべてのJavaScriptと同じくらい安全で堅実です。

11
Thevs

他の答えと一緒に、evalステートメントが高度な最小化を持つことができるとは思わない。

7
Paul Mendoza

これは潜在的なセキュリティリスクであり、実行の範囲が異なり、コードを実行するためのまったく新しいスクリプト環境を作成するため、非常に非効率的です。より詳しい情報はここを見てください: eval

それは非常に便利ですが、モデレートと一緒に使用すると多くの優れた機能を追加できます。

6
Tom

私はこの議論は古いことを知っていますが、私は本当に Googleによるこの アプローチが好きで、その気持ちを他の人たちと共有したいと思いました;)

他のことは、より良いあなたがより多くをあなたが理解しようと試みるということです、そして最後にあなたはただ誰かがそう言ったからといって何かが良いか悪いかを信じません:)これは非常に感動的です 自分で考えさせてくれたvideo :)グッドプラクティスはいいのですが、きちんと使ってはいけません:)

5
op1ekun

評価されているコードが信頼できるソース(通常あなた自身のアプリケーション)からのものであることを100%確信していない限り、それはあなたのシステムをクロスサイトスクリプティング攻撃にさらす確実な方法です。

5
John Topley

使用しているコンテキストがわかっていれば、必ずしもそれほど悪いわけではありません。

アプリケーションがeval()を使用して、 XMLHttpRequest から信頼されたサーバーサイドで作成されたJSONからオブジェクトを作成する場合コード、それはおそらく問題ではありません。

信頼できないクライアントサイドのJavaScriptコードでは、それほどうまくいきません。あなたがeval()を実行しているものが合理的なソースから来たものであれば、大丈夫です。

5
MarkR

それはあなたのセキュリティに対する信頼のレベルを大いに低下させます。

4
David Plumpton

ユーザに論理関数を入力してORのANDを評価させたい場合は、JavaScriptのeval関数が最適です。 2つの文字列とeval(uate) string1 === string2などを受け入れることができます。

4
Ian

あなたのコードの中でeval()の使用に気づいたら、マントラ“ eval()は悪”であることを忘れないでください。

この関数は任意の文字列を受け取り、それをJavaScriptコードとして実行します。問題のコードが事前にわかっている(実行時に決定されていない)場合は、eval()を使用する理由はありません。コードが実行時に動的に生成される場合、eval()を使わずに目的を達成するためのより良い方法がしばしばあります。たとえば、動的プロパティにアクセスするために角括弧表記を使用することがより簡単で優れています。

// antipattern
var property = "name";
alert(eval("obj." + property));

// preferred
var property = "name";
alert(obj[property]);

eval()を使用するとセキュリティ上の影響もあります。なぜなら、改ざんされたコード(たとえばネットワークからのコード)を実行している可能性があるからです。これは、AjaxリクエストからのJSONレスポンスを扱うときの一般的な対パターンです。そのような場合は、ブラウザの組み込みメソッドを使用してJSONレスポンスを解析し、安全で有効であることを確認することをお勧めします。ネイティブにJSON.parse()をサポートしていないブラウザの場合は、JSON.orgのライブラリを使用できます。

setInterval()setTimeout()、およびFunction()コンストラクターに文字列を渡すことは、ほとんどの場合、eval()の使用に似ているため、避けるべきであることを覚えておくことも重要です。

舞台裏では、JavaScriptはまだあなたがプログラミングコードとして渡す文字列を評価して実行しなければなりません:

// antipatterns
setTimeout("myFunc()", 1000);
setTimeout("myFunc(1, 2, 3)", 1000);

// preferred
setTimeout(myFunc, 1000);
setTimeout(function () {
myFunc(1, 2, 3);
}, 1000);

新しいFunction()コンストラクターを使用することはeval()に似ているので注意して取り組むべきです。それは強力な構成要素かもしれませんが、しばしば誤用されます。 eval()を絶対に使用しなければならない場合は、代わりにnew Function()を使用することを検討できます。

New Function()で評価されたコードはローカル関数スコープで実行されるため、評価されるコード内でvarで定義された変数は自動的にグローバルにはならないため、小さな潜在的な利点があります。

自動グローバル化を防ぐもう1つの方法は、eval()呼び出しを即時関数にラップすることです。

3
12345678

ユーザーが送信したコードを実行している場合に発生する可能性があるセキュリティ上の問題に加えて、ほとんどの場合、コードが実行されるたびにコードを再解析することを含まないより良い方法があります。無名関数やオブジェクトプロパティは、ほとんどのevalの使用法を置き換えることができ、はるかに安全で高速です。

2
Matthew Crumley

次世代のブラウザではJavaScriptコンパイラのような種類のものが登場するので、これはもっと問題になるかもしれません。 Evalを介して実行されたコードは、これらの新しいブラウザに対してあなたのJavaScriptの残りの部分と同じようには動作しないかもしれません。誰かが何らかのプロファイリングをするべきです。

2
Brian

eval()は非常に強力で、JSステートメントを実行したり式を評価したりするために使用できます。しかし、質問はeval()の使用に関するものではなく、eval()で実行した文字列が悪意のある者によってどのように影響を受けるのかをいくつか説明してください。最後にあなたは悪意のあるコードを実行することになります。権力には大きな責任があります。だからあなたはそれを使っているのですかそれを賢く使ってください。これはeval()関数とあまり関係がありませんが、この記事にはかなり良い情報があります。 http://blogs.popart.com/2009/07/javascript-injection-attacks/ eval()の基本を探しているならここを見てください: https://developer.mozilla.org/en-US/docs/Web/JavaScript/リファレンス/ Global_Objects/eval

2
Phi

これはevalと、それが悪ではないということについて話している良い記事の1つです: http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-誤解された/

私はあなたが尽きてどこにでもeval()を使い始めるべきだと言っているのではありません。実際、eval()を実行するための良い使用例はほとんどありません。コードの明快さ、デバッグ容易性、そして確かに見落とされてはならないパフォーマンスへの懸念が確かにあります。しかし、eval()が意味をなす場合があるときにそれを使用することを恐れてはいけません。最初に使用しないでください。ただし、eval()が適切に使用されていると、コードが脆弱になったり安全性が低下したりすると、誰かに怖がらせないでください。

2
Amr Elgarhy

それは必ずしも悪い考えではありません。例えばコード生成を考えてみましょう。私は最近 ハイパーバー という名前のライブラリを書きました。これは virtual-domハンドルバーの間の橋渡しをします これは、ハンドルバーテンプレートを解析し、それを ハイパースクリプト に変換することによって行われます。これは、後で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
Wikened

私はこれまでに述べたことに反論するつもりはありませんが、私は(私の知る限りでは)他の方法ではできないというこのeval()の使用を提供します。これをコーディングする他の方法、おそらくそれを最適化する方法がおそらくありますが、これは他の方法がないevalの使用法を説明するために、簡潔にするために手書きの鐘を使わずに手書きで行われます。つまり、(値ではなく)動的に(より正確には)プログラム的に作成されたオブジェクト名です。

//Place this in a common/global JS lib:
var NS = function(namespace){
    var namespaceParts = String(namespace).split(".");
    var namespaceToTest = "";
    for(var i = 0; i < namespaceParts.length; i++){
        if(i === 0){
            namespaceToTest = namespaceParts[i];
        }
        else{
            namespaceToTest = namespaceToTest + "." + namespaceParts[i];
        }

        if(eval('typeof ' + namespaceToTest) === "undefined"){
            eval(namespaceToTest + ' = {}');
        }
    }
    return eval(namespace);
}


//Then, use this in your class definition libs:
NS('Root.Namespace').Class = function(settings){
  //Class constructor code here
}
//some generic method:
Root.Namespace.Class.prototype.Method = function(args){
    //Code goes here
    //this.MyOtherMethod("foo"));  // => "foo"
    return true;
}


//Then, in your applications, use this to instantiate an instance of your class:
var anInstanceOfClass = new Root.Namespace.Class(settings);

編集:ところで、私は(これまで指摘されたすべてのセキュリティ上の理由から)あなたがユーザ入力に基づいてオブジェクト名をベースにすることをお勧めしません。私はあなたがそれをしたいと思うどんな正当な理由も想像できません。それでも、それは良い考えではないだろうと私はそれを指摘したいと思いました:)

1
Carnix

ブラウザで実行されるjavascriptでeval()を使用しても、実際には問題にならないということです。(警告)

最近のすべてのブラウザには開発者コンソールがあり、任意のjavascriptを実行できます。セミスマート開発者は自分のJSソースを見て、必要なことを開発者コンソールに追加して、必要なことを行えます。

*サーバーエンドポイントがユーザー指定の値の正しい検証と正当性を確認している限り、クライアントサイドのJavaScriptで何が解析され評価されるかは重要ではありません。

PHPでeval()を使用するのが適切かどうかを尋ねた場合、その答えはホワイトリストを除いてNOです。 evalステートメントに渡される可能性のある任意の値.

1
Adam Copley

JavaScriptエンジンには、コンパイル段階で実行されるパフォーマンスの最適化がいくつかあります。これらの中には、コードを字句解析するときにコードを本質的に静的に分析し、すべての変数と関数の宣言がどこにあるかを事前に決定できるようにしたものです。

しかし、エンジンがコード内で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

1
hkasera

Garbage collection

ブラウザのガベージコレクションは、評価されたコードをメモリから削除できるかどうかわからないため、ページがリロードされるまで保存されたままになります。ユーザーが間もなくページにアクセスするだけであればそれほど悪くはありませんが、webappの問題になる可能性があります。

ここに問題をデモするスクリプトがあります

https://jsfiddle.net/CynderRnAsh/qux1osnw/

document.getElementById("evalLeak").onclick = (e) => {
  for(let x = 0; x < 100; x++) {
    eval(x.toString());
  }
};

上記のコードと同じくらい簡単なことで、アプリが終了するまで少量のメモリが保存されます。これは、評価されたスクリプトが巨大な関数であり、間隔を置いて呼び出された場合はさらに悪化します。

0
J D