eval関数は、コードを動的に生成するための強力で簡単な方法なので、警告は何ですか?
eval関数は、コードを動的に生成するための強力で簡単な方法なので、警告は何ですか?
回答:
evalを不適切に使用すると、インジェクション攻撃のコードが開かれます
デバッグはより困難になる可能性があります(行番号なしなど)
eval'dコードの実行が遅くなる(eval'dコードをコンパイル/キャッシュする機会がない)
編集:@Jeff Waldenがコメントで指摘しているように、今日の#3は2008年よりも真実ではありません。ただし、コンパイルされたスクリプトの一部のキャッシュが発生する可能性がありますが、これは変更なしで繰り返し評価されたスクリプトに限定されます。より可能性の高いシナリオは、毎回わずかな変更が加えられたためにキャッシュできなかったスクリプトを評価している場合です。いくつかの評価済みコードの実行速度が遅いとしましょう。
badHackerGuy'); doMaliciousThings();
ように設定します。ユーザー名を取得してスクリプトに連結し、他のユーザーのブラウザーで評価すると、自分のマシンで任意のJavaScriptを実行できます(たとえば、投稿を+1するように強制します)。私のサーバーなどにデータを投稿するなど)
debugger;
ソースコードにステートメントを追加することにより、評価されたコード用にChromeによって作成された仮想ファイルにアクセスできます。これにより、その行でプログラムの実行が停止します。その後、別のJSファイルのようにデバッグブレークポイントを追加できます。
evalは必ずしも悪であるとは限りません。それが完全に適切な場合があります。
ただし、evalは現在および歴史的に、自分が何をしているのかを知らない人々によって大量に過剰に使用されています。これには、残念ながらJavaScriptチュートリアルを書いている人も含まれます。場合によっては、これが実際にセキュリティに影響を及ぼしたり、多くの場合、単純なバグを引き起こしたりすることがあります。したがって、evalに疑問符を付けるためにできることは多ければ多いほど良いです。evalを使用するときはいつでも、何をしているのかを健全性チェックする必要があります。それは、より良く、より安全で、よりクリーンな方法で実行できる可能性があるためです。
あまりにも典型的な例を示すために、変数「potato」に格納されたIDを持つ要素の色を設定するには、次のようにします。
eval('document.' + potato + '.style.color = "red"');
上記の種類のコードの作成者がJavaScriptオブジェクトの動作の基本についての手がかりを持っている場合、evalの必要がなく、リテラルドット名の代わりに角かっこを使用できることに気づいたでしょう。
document[potato].style.color = 'red';
...読みやすく、バグが少ない可能性があります。
(しかし、/ really /が何をしているかを知っている人はこう言うでしょう:
document.getElementById(potato).style.color = 'red';
これは、ドキュメントオブジェクトから直接DOM要素にアクセスするという、おかしな古いトリックよりも信頼性が高くなります。)
JSON.parse()
代わりに使用すべきではありませんeval()
か?
文字列から任意のJavaScript関数を実行できるためだと思います。これを使用すると、不正なコードをアプリケーションに挿入するのが簡単になります。
2つの点が頭に浮かびます:
セキュリティ(ただし、自分で評価する文字列を生成する限り、これは問題ではないかもしれません)
パフォーマンス:実行されるコードが不明になるまで、最適化できません。(javascriptとパフォーマンスについては、確かにSteve Yeggeのプレゼンテーション)
eval
、その小さなコードがユーザーのBブラウザーで実行される
ユーザー入力をeval()に渡すことはセキュリティ上のリスクですが、eval()を呼び出すたびにJavaScriptインタープリターの新しいインスタンスが作成されます。これはリソースを独占する可能性があります。
主に、保守とデバッグがはるかに困難です。のようなものgoto
です。あなたはそれを使うことができますが、それは問題を見つけることを難しくし、後で変更を加える必要があるかもしれない人々を難しくします。
心に留めておくべきことの1つは、eval()を使用して、他の制限された環境でコードを実行できることです。特定のJavaScript関数をブロックするソーシャルネットワーキングサイトは、evalブロックに分割することによってだまされる可能性があります-
eval('al' + 'er' + 't(\'' + 'hi there!' + '\')');
そのため、JavaScriptのコードを実行する場合、それが許可されていない可能性がある場合(Myspace、私はあなたを見ている...)、eval()は便利なトリックになる可能性があります。
ただし、上記のすべての理由により、完全に制御できる独自のコードには使用しないでください。必要ないだけでなく、「トリッキーなJavaScriptハッキング」シェルフに委ねられます。
[]["con"+"struc"+"tor"]["con"+"struc"+"tor"]('al' + 'er' + 't(\'' + 'hi there!' + '\')')()
(cgiまたは入力を介して)動的コンテンツをeval()に許可しない限り、ページ内の他のすべてのJavaScriptと同じように安全で確実です。
評価されるコードが信頼できるソース(通常は独自のアプリケーション)からのものであることを100%確信していない限り、システムをクロスサイトスクリプティング攻撃にさらす確実な方法です。
私はこの議論が古いことを知っていますが、Googleによるこのアプローチが本当に好きで、その気持ちを他の人と共有したいと思いました;)
他の事は、より良いあなたがより多くのあなたが理解しようと最終的にはあなただけでは何かが誰かがそう言ったからといっ:)これは非常にインスピレーションで良いか悪いかであるとは考えていない得るということであるビデオ私は自分自身でもっと考えるように役立ちました:)良い習慣は良いですが、気にしないでください:)
使用しているコンテキストがわかっていれば、必ずしも悪いことではありません。
アプリケーションが、信頼されたサーバー側のコードによって作成されたeval()
、XMLHttpRequestから独自のサイトに返されたJSONからオブジェクトを作成するために使用している場合は、おそらく問題ありません。
信頼されていないクライアント側のJavaScriptコードは、とにかくそんなに多くはできません。実行eval()
しているものが合理的なソースからのものであれば、問題ありません。
これにより、セキュリティに対する信頼度が大幅に低下します。
コードで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);
new Function()コンストラクターの使用はeval()に似ており、注意してアプローチする必要があります。それは強力な構成要素になる可能性がありますが、しばしば誤用されます。どうしても使用する必要がある場合eval()
、代わりにnew Function()の使用を検討できます。
new Function()で評価されるコードはローカル関数スコープで実行されるため、評価されるコードでvarを使用して定義された変数が自動的にグローバルになることはないため、小さな潜在的な利点があります。
自動グローバルを防ぐ別の方法は、eval()
呼び出しを即時関数にラップする
ことです。
ユーザーが送信したコードを実行する場合に起こり得るセキュリティの問題に加えて、ほとんどの場合、実行されるたびにコードを再解析することを含まないより良い方法があります。匿名関数またはオブジェクトプロパティは、evalのほとんどの使用法を置き換えることができ、より安全で高速です。
これは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-attacks/ eval()の基本を探している場合こちらをご覧ください:https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/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
それは常に悪い考えではありません。たとえば、コード生成を見てみましょう。私は最近、virtual-domとハンドルバー間のギャップを埋める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()
ような状況では、生成された文字列を一度解釈して、実行可能ファイルの出力を何度も再利用する必要があるため、のパフォーマンスは問題になりません。
ここで興味があれば、コード生成がどのように行われたかを確認できます。
eval()
ブラウザで実行されるjavascriptで使用する場合、それは実際には問題にならないとまで言っておきます。*(警告)
最近のすべてのブラウザーには、とにかく任意のJavaScriptを実行できる開発者コンソールがあり、セミスマート開発者はJSソースを調べて、必要なあらゆるものを開発者コンソールに入れて、希望することを実行できます。
*サーバーエンドポイントにユーザーが入力した値の正しい検証とサニタイズがある限り、クライアント側のJavaScriptで何が解析され、評価されるかは問題ではありません。
eval()
ただし、PHPでの使用に適しているかどうかを尋ねる場合、evalステートメントに渡される可能性のある値をホワイトリストに登録しない限り、答えはNOです。
私はこれまでに述べたことに反論するつもりはありませんが、(私が知る限り)他の方法では実行できない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);
編集:ところで、私は(これまでに指摘されたすべてのセキュリティ上の理由から)オブジェクト名をユーザー入力に基づくことはお勧めしません。あなたがそれをしたいと思うであろう正当な理由を私は想像することはできません。それでも、それは良い考えではないことを指摘したいと思いました:)
namespaceToTest[namespaceParts[i]]
ここではevalは必要ありません。そのためif(typeof namespaceToTest[namespaceParts[i]] === 'undefined') { namespaceToTest[namespaceParts[i]] = {};
唯一の違いはelse namespaceToTest = namespaceToTest[namespaceParts[i]];
ガベージコレクション
ブラウザのガベージコレクションは、evalされたコードをメモリから削除できるかどうかわからないため、ページがリロードされるまでコードを保存し続けます。ユーザーがページにすぐにいる場合でもそれほど悪くはありませんが、webappにとっては問題になる可能性があります。
これが問題をデモするスクリプトです
https://jsfiddle.net/CynderRnAsh/qux1osnw/
document.getElementById("evalLeak").onclick = (e) => {
for(let x = 0; x < 100; x++) {
eval(x.toString());
}
};
上記のコードのように単純なものでは、アプリが終了するまで少量のメモリが保存されます。これは、評価されたスクリプトが巨大な関数であり、間隔で呼び出される場合はさらに悪化します。