288

ユーザーが入力した関数を解析する JavaScript コードをいくつか書いています (スプレッドシートのような機能のため)。数式を解析したら、それを JavaScript に変換して実行し、結果を得ることができeval()ました。

しかし、それはeval()悪いことなので、回避できる場合は常に使用を避けてきました (そして、正しいか間違っているかにかかわらず、評価されるコードはユーザーによって変更される可能性があるため、JavaScript ではさらに悪いと常に考えていました)。 )。

では、いつ使用してもよろしいでしょうか?

4

26 に答える 26

276

あなたの質問の前提に対処するために少し時間を取りたいと思います.eval()は「」です. プログラミング言語の人々が使用する「悪」という言葉は、通常、「危険な」、より正確には「単純に見えるコマンドで多くの害を及ぼす可能性がある」という意味です。では、いつ危険なものを使用してもよいのでしょうか? 危険が何であるかを知っていて、適切な予防策を講じているとき。

要するに、eval() の使用における危険性を見てみましょう。他のすべてのものと同様に、おそらく多くの小さな危険が隠れていますが、eval() が悪と見なされる理由である 2 つの大きなリスクは、パフォーマンスとコード インジェクションです。

  • パフォーマンス - eval() はインタープリター/コンパイラーを実行します。コードがコンパイルされている場合、実行時におそらく重いコンパイラを呼び出す必要があるため、これは大ヒットです。ただし、JavaScript は依然としてほとんどがインタープリター型言語であるため、一般的なケースでは、eval() の呼び出しはパフォーマンスに大きな影響を与えません (ただし、以下の特定の発言を参照してください)。
  • コード インジェクション - eval() は、昇格された権限で一連のコードを実行する可能性があります。たとえば、管理者/ルートとして実行されているプログラムは、ユーザー入力を eval() したくありません。その入力は潜在的に「rm -rf /etc/important-file」またはそれより悪い可能性があるためです。繰り返しになりますが、ブラウザの JavaScript にはそのような問題はありません。プログラムはいずれにせよユーザー自身のアカウントで実行されるからです。サーバーサイドの JavaScript には、その問題がある可能性があります。

あなたの特定のケースに。私が理解していることから、あなたは自分で文字列を生成しているので、「rm -rf something-important」のような文字列が生成されないように注意していると仮定すると、コードインジェクションのリスクはありません (ただし、覚えておいてください、それは非常に非常に一般的なケースでこれを保証するのは難しい)。また、ブラウザで実行している場合、コード インジェクションのリスクはかなり小さいと思います。

パフォーマンスに関しては、コーディングの容易さと比較する必要があります。数式を解析している場合は、別のパーサー (eval() 内のパーサー) を実行するのではなく、解析中に結果を計算した方がよいというのが私の意見です。しかし、eval() を使用してコーディングする方が簡単な場合があり、パフォーマンスへの影響はおそらく目立たないでしょう。この場合の eval() は、時間を節約できる可能性のある他の関数よりも悪くないようです。

于 2008-10-13T15:40:39.707 に答える
77

eval()悪ではありません。または、そうである場合、リフレクション、ファイル/ネットワークI / O、スレッド化、およびIPCが他の言語で「悪」であるのと同じように悪です。

目的のために、手動による解釈よりも高速である場合eval()、またはコードをより単純にする、またはより明確にする場合は、それを使用する必要があります。どちらでもない場合は、すべきではありません。そのような単純な。

于 2008-10-13T14:44:03.970 に答える
61

ソースを信頼するとき。

JSON の場合、ソースを改ざんすることは多かれ少なかれ困難です。なぜなら、それはあなたが制御する Web サーバーからのものだからです。JSON 自体にユーザーがアップロードしたデータが含まれていない限り、eval を使用することに大きな欠点はありません。

それ以外の場合はすべて、eval() に渡す前に、ユーザーが提供したデータが私のルールに準拠していることを確認するために多大な努力を払います。

于 2008-10-13T14:34:37.687 に答える
25

本当の人を手に入れましょう:

  1. 現在、すべての主要なブラウザーには、ハッカーになる可能性のあるユーザーが任意の値の関数を呼び出すために豊富に使用できる組み込みのコンソールがあります。

  2. 2000行のJavaScriptをコンパイルするのに0.2秒かかる場合、4行のJSONを評価すると、パフォーマンスがどのように低下​​しますか?

「評価は悪である」というクロックフォードの説明でさえ弱い。

evalは悪です。eval関数はJavaScriptの最も誤用されている機能です。それを避けてください

クロックフォード自身が「この種の陳述は不合理な神経症を引き起こす傾向がある。それを買わないでください」と言うかもしれないように。

evalを理解し、それがいつ役立つかを知ることは、はるかに重要です。たとえば、evalは、ソフトウェアによって生成されたサーバー応答を評価するための賢明なツールです。

ところで:Prototype.jsはevalを直接5回呼び出します(evalJSON()とevalResponse()を含む)。jQueryはそれをparseJSONで(関数コンストラクターを介して)使用します。

于 2010-04-13T20:49:38.817 に答える
19

私はCrockfordのアドバイスに従う傾向がありeval()、それを完全に避けます。それを必要とすると思われる方法でさえ、そうではありません。たとえば、はsetTimeout()evalではなく関数を渡すことができます。

setTimeout(function() {
  alert('hi');
}, 1000);

信頼できるソースであっても、JSONによって返されるコードが文字化けしている可能性があるため、使用しません。

于 2008-10-13T14:36:50.647 に答える
6

Chrome (v28.0.1500.72) でデバッグすると、変数がクロージャーを生成するネストされた関数で使用されていない場合、変数がクロージャーにバインドされていないことがわかりました。おそらく、これは JavaScript エンジンの最適化です。

BUT :eval()がクロージャーを引き起こす関数内で使用されると、たとえそれらがまったく使用されていなくても、外側の関数のすべての変数がクロージャーにバインドされます。それによってメモリリークが発生する可能性があるかどうかをテストする時間があれば、下にコメントを残してください。

ここに私のテストコードがあります:

(function () {
    var eval = function (arg) {
    };

    function evalTest() {
        var used = "used";
        var unused = "not used";

        (function () {
            used.toString();   // Variable "unused" is visible in debugger
            eval("1");
        })();
    }

    evalTest();
})();

(function () {
    var eval = function (arg) {
    };

    function evalTest() {
        var used = "used";
        var unused = "not used";

        (function () {
            used.toString();   // Variable "unused" is NOT visible in debugger
            var noval = eval;
            noval("1");
        })();
    }

    evalTest();
})();

(function () {
    var noval = function (arg) {
    };

    function evalTest() {
        var used = "used";
        var unused = "not used";

        (function () {
            used.toString();    // Variable "unused" is NOT visible in debugger
            noval("1");
        })();
    }

    evalTest();
})();

ここで指摘したいのは、eval() は必ずしもネイティブeval()関数を参照してはならないということです。それはすべて関数の名前に依存しますeval()したがって、エイリアス名を使用してネイティブを呼び出す場合(たとえばvar noval = eval;、内部関数で) 、クロージャーの一部であるはずの変数を参照するとnoval(expression);、 の評価が失敗する可能性がありますが、実際にはそうではありません。expression

于 2013-07-23T12:03:40.710 に答える
6

Eval は、コードのテンプレート化に使用されるコンパイルを補完します。テンプレート化とは、開発速度を向上させる便利なテンプレート コードを生成する単純化されたテンプレート ジェネレーターを作成することを意味します。

開発者が EVAL を使用しないフレームワークを作成しましたが、開発者は私たちのフレームワークを使用し、そのフレームワークは EVAL を使用してテンプレートを生成する必要があります。

EVAL のパフォーマンスは、次の方法を使用して向上させることができます。スクリプトを実行する代わりに、関数を返す必要があります。

var a = eval("3 + 5");

次のように編成する必要があります。

var f = eval("(function(a,b) { return a + b; })");

var a = f(3,5);

f をキャッシュすると、確かに速度が向上します。

また、Chrome では、このような機能を非常に簡単にデバッグできます。

セキュリティに関しては eval を使っても使わなくてもほとんど変わらないので、

  1. まず、ブラウザはサンドボックス内のスクリプト全体を呼び出します。
  2. EVAL で悪であるコードは、ブラウザー自体でも悪です。攻撃者または誰でも簡単に DOM にスクリプト ノードを挿入し、何かを評価できれば何でも実行できます。EVAL を使用しなくても違いはありません。
  3. 害があるのは、主にサーバー側のセキュリティの悪さです。ほとんどの攻撃は、Cookie の検証が不十分であるか、サーバーでの ACL の実装が不十分であることが原因です。
  4. 最近の Java の脆弱性などは、Java のネイティブ コードにありました。JavaScript はサンドボックス内で実行するように設計されていますが、アプレットはサンドボックスの外で実行するように設計されており、脆弱性やその他多くの原因となる証明書などがあります。
  5. ブラウザを模倣するコードを書くことは難しくありません。必要なのは、お気に入りのユーザー エージェント文字列を使用してサーバーに HTTP 要求を送信することだけです。いずれにせよ、すべてのテスト ツールはブラウザを模倣します。攻撃者があなたに危害を加えたい場合、EVAL は最後の手段です。サーバー側のセキュリティに対処する方法は他にもたくさんあります。
  6. ブラウザ DOM は、ファイルやユーザー名にアクセスできません。実際、 eval がアクセスできるマシンには何もありません。

サーバー側のセキュリティが万全で、どこからでも攻撃できるのであれば、EVAL について心配する必要はありません。前述したように、EVAL が存在しない場合、攻撃者はブラウザーの EVAL 機能に関係なく、サーバーにハッキングするための多くのツールを持っています。

Eval は、事前に使用されていないものに基づいて複雑な文字列処理を行うためのテンプレートを生成する場合にのみ適しています。たとえば、私は好むでしょう

"FirstName + ' ' + LastName"

とは対照的に

"LastName + ' ' + FirstName"

データベースから取得でき、ハードコードされていない私の表示名として。

于 2013-04-19T10:18:08.900 に答える
4

私は人々が eval を使用しないことを提唱しているのを見ました。これはだからです。しかし、同じ人々が Function と setTimeout を動的に使用しているのを見たので、彼らはフードの下でeval を使用しています:D

ところで、サンドボックスが十分にわからない場合 (たとえば、コード インジェクションを許可するサイトで作業している場合) eval が最後の問題です。セキュリティの基本的なルールは、すべての入力が悪であるということですが、JavaScript の場合はJavaScript自体が悪になる可能性があります。なぜなら、JavaScript では任意の関数を上書きでき、実際の関数を使用しているかどうかを確認できないためです。悪意のあるコードが目の前で始まったら、JavaScript の組み込み関数を信頼できません:D

さて、この投稿のエピローグは次のとおりです。

本当に必要な場合 (80% の時間 eval は必要ありません) で、何をしているのか確信がある場合は、eval (またはより良い Function ;) を使用してください)、クロージャーと OOP は、80/90% をカバーします別の種類のロジックを使用して eval を置き換えることができる場合、残りは動的に生成されたコード (たとえば、インタープリターを作成している場合) であり、JSON の評価について既に述べたように (ここではクロックフォードの安全な評価を使用できます ;) )

于 2008-10-13T17:24:09.700 に答える
2

eval()を使用する必要があるのは、動的JSをオンザフライで実行する必要がある場合のみです。サーバーから非同期でダウンロードするJSについて話しています...

...そして10の9倍は、リファクタリングによってそれを簡単に回避できます。

于 2008-10-13T14:35:22.100 に答える
2

Eval は悪用ではなく、悪用されているだけです。

それに入るコードを作成した場合、またはそれを信頼できる場合は、問題ありません。eval ではユーザー入力は問題にならないという話をし続けています。なんか~

サーバーに送られるユーザー入力があり、クライアントに返される場合、そのコードはサニタイズされずに eval で使用されます。おめでとうございます。パンドラの箱を開けて、ユーザー データを誰にでも送信できるようにしました。

eval の場所によっては、多くの Web サイトで SPA が使用されており、eval を使用すると、ユーザーがアプリケーション内部に簡単にアクセスできなくなる可能性があります。今では、その eval にテープで貼り付けてデータを再び盗むことができる偽のブラウザ拡張機能を作成できます。

eval を使用する意味を理解する必要があります。コードの生成は、そのようなことを行うメソッドを単純に作成したり、オブジェクトを使用したりできる場合には理想的ではありません。

eval を使用した良い例です。サーバーは、作成した swagger ファイルを読み取っています。URL パラメーターの多くは、{myParam}. 多数のエンドポイントがあるため、複雑な置換を行うことなく、URL を読み取り、それらをテンプレート文字列に変換したいと考えています。だからあなたはこのようなことをするかもしれません。これは非常に単純な例であることに注意してください。

const params = { id: 5 };

const route = '/api/user/{id}';
route.replace(/{/g, '${params.');

// use eval(route); to do something

于 2020-01-17T18:02:01.680 に答える
1

evalが正しい選択になることはめったにありません。スクリプトを連結してオンザフライで実行することによって、達成する必要があることを達成できる例は数多くあるかもしれませんが、通常は、はるかに強力で保守しやすい手法を自由に使用できます: 連想配列表記法 (obj["prop"]は と同じですobj.prop) 、クロージャー、オブジェクト指向手法、関数型手法 - 代わりにそれらを使用してください。

于 2009-02-11T13:06:49.723 に答える
1

クライアント スクリプトに関する限り、セキュリティの問題は議論の余地があると思います。ブラウザにロードされるものはすべて操作の対象となるため、そのように扱う必要があります。JavaScript コードを実行したり、ブラウザーの URL バーなどの DOM 内のオブジェクトを操作したりするためのはるかに簡単な方法がある場合は、eval() ステートメントを使用してもリスクはありません。

javascript:alert("hello");

誰かが自分の DOM を操作したい場合、私は「離れてください」と言います。あらゆる種類の攻撃を防ぐためのセキュリティは、常にサーバー アプリケーションの責任である必要があります。

実用的な観点からは、別の方法で処理できる状況で eval() を使用するメリットはありません。ただし、eval を使用する必要がある特定のケースがあります。その場合、ページを爆破するリスクなしに確実に実行できます。

<html>
    <body>
        <textarea id="output"></textarea><br/>
        <input type="text" id="input" />
        <button id="button" onclick="execute()">eval</button>

        <script type="text/javascript">
            var execute = function(){
                var inputEl = document.getElementById('input');
                var toEval = inputEl.value;
                var outputEl = document.getElementById('output');
                var output = "";

                try {
                    output = eval(toEval);
                }
                catch(err){
                    for(var key in err){
                        output += key + ": " + err[key] + "\r\n";
                    }
                }
                outputEl.value = output;
            }
        </script>
    <body>
</html>
于 2009-07-29T16:52:55.463 に答える
0

関数に渡されるコードを完全に制御できる場合は、これを使用しても問題ありませんeval

于 2008-10-13T14:36:17.310 に答える
0

可能であれば、テスト中のみ。また、eval()は、他の特殊なJSONなどのエバリュエーターよりもはるかに遅いことに注意してください。

于 2008-10-13T14:40:27.427 に答える
0

コードのソースがあなたまたは実際のユーザーからのものであることが確実である限り、 eval() を使用しない理由はありません。彼は eval() 関数に送信されるものを操作できますが、それはセキュリティ上の問題ではありません。なぜなら、彼は Web サイトのソース コードを操作できるため、JavaScript コード自体を変更できるからです。

それで...いつeval()を使用しないのですか?Eval() は、第三者が変更する可能性がある場合にのみ使用しないでください。クライアントとサーバー間の接続を傍受するのと同じです (ただし、それが問題になる場合は HTTPS を使用してください)。フォーラムのように他の人が書いたコードを解析するために eval() を使うべきではありません。

于 2008-10-13T14:47:41.840 に答える
0

私の使用例evalimport

普段のやり方。

var components = require('components');
var Button = components.Button;
var ComboBox = components.ComboBox;
var CheckBox = components.CheckBox;
...
// That quickly gets very boring

しかしeval、少しヘルパー関数を使用すると、見栄えが大幅に向上します。

var components = require('components');
eval(importable('components', 'Button', 'ComboBox', 'CheckBox', ...));

importableのように見えるかもしれません (このバージョンは具体的なメンバーのインポートをサポートしていません)。

function importable(path) {
    var name;
    var pkg = eval(path);
    var result = '\n';

    for (name in pkg) {
        result += 'if (name !== undefined) throw "import error: name already exists";\n'.replace(/name/g, name);
    }

    for (name in pkg) {
        result += 'var name = path.name;\n'.replace(/name/g, name).replace('path', path);
    }
    return result;
}
于 2013-04-30T06:59:29.917 に答える
0

それが本当に必要な場合、評価は悪ではありません。しかし、私が遭遇した eval の使用の 99.9% は必要ありません(setTimeout のものは含まれません)。

私にとって、悪はパフォーマンスの問題でも、セキュリティの問題でもありません (間接的には両方の問題です)。このような不必要な eval の使用はすべて、メンテナンス地獄に追加されます。リファクタリング ツールは破棄されます。コードを探すのは大変です。これらの evals の予期しない影響は非常に大きいです。

于 2008-12-20T18:50:23.520 に答える
-1

私の信念は、evalはクライアント側のWebアプリケーションにとって非常に強力な機能であり、安全です...JavaScriptと同じくらい安全ですがそうではありません。:-)セキュリティの問題は本質的にサーバー側の問題です。これは、Firebugなどのツールを使用すると、任意のJavaScriptアプリケーションを攻撃できるためです。

于 2009-01-15T07:34:08.467 に答える
-1

JavaScript の eval() が悪ではないのはいつですか?

私は常にeval の使用を思いとどまらせようとしています。ほとんどの場合、よりクリーンで保守しやすいソリューションが利用可能です。JSON 解析でも評価は必要ありません。Evalはメンテナンス地獄に追加されます。当然のことながら、ダグラス・クロックフォードのよ​​うな巨匠から眉をひそめられています。

しかし、私はそれを使用する必要がある1つの例を見つけました:

式を渡す必要がある場合。

たとえば、一般的なgoogle.maps.ImageMapTypeオブジェクトを構築する関数がありますが、レシピを伝える必要があります。zoomおよびcoordパラメータからタイル URL を構築する方法は次のとおりです。

my_func({
    name: "OSM",
    tileURLexpr: '"http://tile.openstreetmap.org/"+b+"/"+a.x+"/"+a.y+".png"',
    ...
});

function my_func(opts)
{
    return new google.maps.ImageMapType({
        getTileUrl: function (coord, zoom) {
            var b = zoom;
            var a = coord;
            return eval(opts.tileURLexpr);
        },
        ....
    });
}
于 2012-04-25T23:43:03.113 に答える
-1

Eval は、マクロがない場合のコード生成に役立ちます。

(ばかげた) 例として、Brainfuckコンパイラを作成している場合、一連の命令を文字列として実行する関数を構築し、それを評価して関数を返したいと思うでしょう。

于 2013-07-12T19:54:19.057 に答える
-5

解析関数 (jQuery.parseJSON など) を使用して JSON 構造を解析する場合、JSON ファイルの完全な構造が想定されます (各プロパティ名は二重引用符で囲まれています)。ただし、JavaScript はより柔軟です。したがって、eval() を使用して回避できます。

于 2012-05-17T23:00:22.590 に答える