JavaScript の eval 関数を使用することが悪い考えであるのはなぜですか?

StackOverflow https://stackoverflow.com/questions/86513

  •  01-07-2019
  •  | 
  •  

質問

eval 関数はコードを動的に生成する強力かつ簡単な方法ですが、注意点は何ですか?

役に立ちましたか?

解決

  1. 不適切な使用 評価 注入攻撃のためにコードを開きます

  2. デバッグ より挑戦的になる可能性があります(ライン番号などはありません。)

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

編集:@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 つの点が思い浮かびます。

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

  2. パフォーマンス:実行されるコードが不明になるまで、最適化することはできません。(JavaScript とパフォーマンスについては確かに スティーブ・イエッグ氏のプレゼンテーション)

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

通常、これは eval ユーザー入力を渡す場合にのみ問題になります。

主に、保守とデバッグが非常に困難です。それはまるで goto. 。使用することはできますが、問題を見つけるのが難しくなり、後で変更が必要になる可能性のある人にとっては負担がかかります。

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

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

したがって、他の方法では許可されない可能性のある JavaScript コードを実行しようとしている場合 (私のスペース, 、私はあなたを見ています...) その場合、 eval() は便利なトリックになる可能性があります。

ただし、上で述べたすべての理由から、完全に制御できる独自のコードには使用しないでください。単に必要ではないだけであり、「トリッキーな JavaScript ハック」棚に追いやられたほうがよいでしょう。

eval() を動的コンテンツ (CGI または入力を通じて) にしない限り、ページ内の他のすべての JavaScript と同様に安全かつ堅牢です。

残りの回答と同様に、eval ステートメントでは高度な最小化を行うことはできないと思います。

これはセキュリティ リスクの可能性があり、実行範囲が異なり、コードを実行するためのまったく新しいスクリプト環境が作成されるため、非常に非効率的です。詳細については、ここを参照してください。 評価.

ただし、これは非常に便利であり、適度に使用すると、多くの優れた機能を追加できます。

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

この議論が古いことは承知していますが、私はとても気に入っています これ Google からのアプローチで、その気持ちを他の人たちと共有したいと思いました ;)

もう1つのことは、あなたがより良く理解しようとするほど、誰かがそう言ったからといって何かが良いか悪いかを信じないということです:)これは非常にインスピレーションです ビデオ それは私がもっと自分で考えるのに役立ちました:) 良い実践は良いですが、それをむやみに使用しないでください:)

どのようなコンテキストで使用しているかを知っていれば、必ずしもそれほど悪いものではありません。

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

いずれにしても、信頼できないクライアント側の JavaScript コードでは、それほど多くのことはできません。実行しているものを提供する eval() は合理的な情報源から来ています、大丈夫です。

セキュリティに対する信頼度が大幅に低下します。

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

コード内で 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() の使用を検討できます。

新しい関数()で評価されたコードがローカル関数範囲で実行されるため、わずかな潜在的な利点があるため、評価されるコードのVARで定義された変数は自動的にグローバルになりません。

自動グローバルを防ぐもう 1 つの方法は、eval() 即時関数を呼び出します。

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

次世代のブラウザが何らかの種類の JavaScript コンパイラを搭載するようになるにつれて、これはさらに問題になる可能性があります。Eval 経由で実行されるコードは、これらの新しいブラウザーに対して JavaScript の残りの部分ほどパフォーマンスが良くない可能性があります。誰かがプロファイリングをする必要があります。

これは、eval について、そしてそれが悪ではないことについて説明している良い記事の 1 つです。http://www.nczonline.net/blog/2013/06/25/eval-isnt-evil-just-misunderstood/

私はあなたが使い果たして、どこでもeval()を使用し始めるべきだと言っているのではありません。実際、eval()を実行するための適切なユースケースはほとんどありません。コードの明確さ、デバッグ性、そして見落とされるべきではない確かにパフォーマンスには間違いなく懸念があります。ただし、eval()が理にかなっている場合がある場合、使用することを恐れてはいけません。最初に使用しないでください。しかし、eval()が適切に使用されると、誰もがあなたのコードがより脆弱であるか、安全でないと思わせないでください。

eval() は非常に強力で、JS ステートメントの実行や式の評価に使用できます。しかし、質問は eval() の使用法に関するものではなく、eval() で実行する文字列が悪意のある者によってどのような影響を受けるかについて少し述べてみましょう。最終的には悪意のあるコードを実行することになります。権力には大きな責任が伴います。したがって、使用している場合は賢明に使用してください。これは eval() 関数とはあまり関係ありませんが、この記事にはかなり良い情報が記載されています。 http://blogs.popart.com/2009/07/javascript-injection-攻撃/eval() の基本を探している場合は、ここを参照してください。https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval

これまでに述べたことに反論するつもりはありませんが、(私の知る限り)他の方法では実行できない 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);

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

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

それは必ずしも悪い考えではありません。コード生成を例に考えてみましょう。最近、というライブラリを書きました ハイパーバー 間のギャップを埋めるもの 仮想ダム そして ハンドルバー. 。これは、ハンドルバー テンプレートを解析し、次のように変換することで実行されます。 ハイパースクリプト これはその後、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() このような状況では、生成された文字列を一度解釈するだけで済み、その後は実行可能出力を何度も再利用する必要があるため、問題にはなりません。

興味があれば、コード生成がどのように行われたかを確認できます。 ここ.

を使用するかどうかは実際には問題ではないとまで言いたいと思います。 eval() ブラウザで実行される JavaScript 内。*(注意)

最新のブラウザにはすべて、任意の JavaScript を実行できる開発者コンソールがあり、ある程度賢明な開発者であれば、JS ソースを見て、必要な部分を開発コンソールに入力して、やりたいことを実行できます。

*サーバー エンドポイントでユーザーが指定した値が正しく検証およびサニタイズされている限り、クライアント側の JavaScript で何が解析および評価されるかは問題ではありません。

使いやすいかと言われたら eval() ただし、PHP では答えは次のとおりです。 いいえ, 、あなたがしない限り、 ホワイトリスト eval ステートメントに渡すことができる任意の値。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top