質問

書い一部にJavaScriptコードを解析してユーザー入力機能(スプレッドシート機能付き)を構文解析の式I に変換するJavaScriptラ eval() で利回りの結果です。

しかし、私はいつも遠ざか eval() 出すことができれば回避で過ごしたい人にはお勧めで悪、正又は誤ちもますます悪JavaScriptのコードに対する評価が変更になるユーザ)。

そこでOKを使用すればいいですか?

役に立ちましたか?

解決

あなたの質問の前提に取り組もうと思います-eval()は" evil "です。プログラミング言語の人々が使用する「 evil 」という言葉は、通常「危険」を意味し、より正確には「単純なコマンドで多くの害を引き起こす可能性がある」ことを意味します。それで、いつ危険なものを使用しても大丈夫ですか?危険とは何か、そして適切な予防策を講じているとき。

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

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

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

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

他のヒント

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

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

ソースを信頼するとき。

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

他のすべてのケースでは、ユーザーが提供したデータがeval()に送信される前に、ルールに準拠するように最大限努力します。

本物の人々を取得しましょう:

  1. すべての主要なブラウザにはビルトインコンソールがあり、ハッカーとなるユーザーは豊富な機能を使用して任意の値の関数を呼び出すことができます。 / p>

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

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

  

evalはEvilです。eval関数は、   JavaScript。避けてください

クロックフォード自身が言うように、"この種の発言は不合理な神経症を引き起こす傾向があります。購入しないでください。"

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

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

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

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

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

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"

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

evil であるため、evalを使用しないことを支持する人々を見ましたが、同じ人々がFunctionおよびsetTimeoutを動的に使用するので、フードの下でeval を使用します: D

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

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

本当に必要な場合(evalは不要の80%の時間)必要な場合は、evalを使用するだけです(またはより良い機能;))、クロージャーとOOPは、evalが別の種類のロジックを使用して置き換えられる場合の80/90%をカバーし、残りは動的に生成されたコード(たとえば、インタープリターを作成している場合)およびJSONの評価は既に述べています(ここでは、Crockfordの安全な評価を使用できます;))

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

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

テストコードは次のとおりです。

(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 の評価は、クロージャーの一部であるべき変数を参照するときに失敗するかもしれませんが、実際にはそうではありません。

Microsoftは、IEブログのブラウザでeval()が遅い理由を説明しています。 IE + JavaScriptパフォーマンスの推奨事項パート2:JavaScriptコードの非効率性

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

... 10回のうち9回は、リファクタリングすることで簡単に回避できます。

eval 関数に渡されるコードを完全に制御できる場合は、使用しても構いません。

eval が正しい選択になることはめったにありません。スクリプトを連結し、その場で実行することで達成する必要があることを達成できる多くの場合がありますが、通常、より強力で保守可能なテクニックを自由に使用できます:連想配列表記( obj [ " prop"] obj.prop )、クロージャ、オブジェクト指向技術、機能的技術と同じです-代わりに使用してください。

クライアントスクリプトに関する限り、セキュリティの問題は議論の余地があると思います。ブラウザーにロードされたものはすべて操作の対象であり、そのように扱う必要があります。ブラウザのURLバーなど、JavaScriptコードを実行したり、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>

JavaScriptのeval()はいつ悪ではありませんか

常に evalの使用をやめようとしています。ほとんど常に、よりクリーンで保守可能なソリューションが利用可能です。 Eval JSON解析でも不要です。 Eval メンテナンス地獄に追加。理由がないわけではありませんが、ダグラス・クロックフォードなどの巨匠に嫌われています。

しかし、私はそれが使用されるべき ある例を見つけました:

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

たとえば、一般的なを作成する関数があります google.maps.ImageMapType オブジェクトですが、レシピを伝える必要があります。 zoom からタイルURLを構築する方法を教えてくださいcoord パラメータ:

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);
        },
        ....
    });
}

私の使用例 eval: 輸入.

が出てくるんですが、それを通常行われます。

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;
}

サーバー側では、evalはsql、influxdb、mongoなどの外部スクリプトを扱うときに役立ちます。サービスを再デプロイせずに実行時にカスタム検証を実行できる場所。

たとえば、次のメタデータを持つ実績サービス

{
  "568ff113-abcd-f123-84c5-871fe2007cf0": {
    "msg_enum": "quest/registration",
    "timely": "all_times",
    "scope": [
      "quest/daily-active"
    ],
    "query": "`SELECT COUNT(point) AS valid from \"${userId}/dump/quest/daily-active\" LIMIT 1`",
    "validator": "valid > 0",
    "reward_external": "ewallet",
    "reward_external_payload": "`{\"token\": \"${token}\", \"userId\": \"${userId}\", \"amountIn\": 1, \"conversionType\": \"quest/registration:silver\", \"exchangeProvider\":\"provider/achievement\",\"exchangeType\":\"payment/quest/registration\"}`"
  },
  "efdfb506-1234-abcd-9d4a-7d624c564332": {
    "msg_enum": "quest/daily-active",
    "timely": "daily",
    "scope": [
      "quest/daily-active"
    ],
    "query": "`SELECT COUNT(point) AS valid from \"${userId}/dump/quest/daily-active\" WHERE time >= '${today}' ${ENV.DAILY_OFFSET} LIMIT 1`",
    "validator": "valid > 0",
    "reward_external": "ewallet",
    "reward_external_payload": "`{\"token\": \"${token}\", \"userId\": \"${userId}\", \"amountIn\": 1, \"conversionType\": \"quest/daily-active:silver\", \"exchangeProvider\":\"provider/achievement\",\"exchangeType\":\"payment/quest/daily-active\"}`"
  }
}

次に許可する、

  • jsonのリテラル文字列を介したオブジェクト/値の直接インジェクション。テキストのテンプレート化に役立ちます

  • コンパレータとして使用できます。たとえば、CMSでクエストまたはイベントを検証する方法を規則に定めているとします

これの短所:

  • 完全にテストされていない場合、コードにエラーが発生し、サービス内で問題が発生する可能性があります。

  • ハッカーがシステム上でスクリプトを作成できる場合、ほとんど混乱しています。

  • スクリプトを検証する1つの方法は、スクリプトのハッシュを安全な場所に保管することです。したがって、実行前にそれらをチェックできます。

evalが正当化されるケースはまれだと思います。 実際に正当化されたときに使用するよりも、正当化されていると考えて使用する可能性が高くなります。

セキュリティの問題は最もよく知られています。ただし、JavaScriptはJITコンパイルを使用し、これはevalでは非常に不十分に動作することにも注意してください。 Evalはコンパイラにとってブラックボックスのようなものであり、JavaScriptはパフォーマンスの最適化とスコープを安全かつ正しく適用するために、事前に(ある程度)コードを予測できる必要があります。場合によっては、パフォーマンスへの影響がeval以外の他のコードにも影響することがあります。

詳細を知りたい場合: https: //github.com/getify/You-Dont-Know-JS/blob/master/scope%20%26%20closures/ch2.md#eval

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

コード生成。最近ハイパーバーというライブラリを作成しました。これは、 virtual-dom および handlebars 。これを行うには、handlebarsテンプレートを解析し、それを hyperscript に変換します。ハイパースクリプトは最初に文字列として生成され、それを返す前に 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回だけ解釈し、実行可能ファイルの出力を何度も再利用する必要があるからです。

好奇心re盛な場合、コード生成がどのように達成されたかを見ることができますこちら

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

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

本当に必要な場合、evalは悪ではありません。しかし、私が偶然見つけたevalの使用の99.9%は不要である必要はありません(setTimeoutを含まない)。

私にとって、悪とはパフォーマンスでもセキュリティの問題でもありません(間接的には両方です)。 evalのこのような不必要な使用はすべて、メンテナンスヘルムに追加されます。リファクタリングツールは廃止されました。コードの検索は困難です。これらの評価の予期しない影響は大勢です。

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

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

(愚かな)例では、 Brainfuck コンパイラを書いている場合、おそらく、一連の命令を文字列として実行する関数を作成し、それを評価して関数を返します。

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

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