JavaScript eval()を代替できる方法

JavaScript eval()を代替できる方法

D
dongAuthor
2 min read

JavaScriptの eval() が危険な理由ず、 new Function() によっお安党に代替する方法を実甚的な䟋ずずもにご玹介したす。セキュリティずパフォヌマンスの䞡方を重芖するヒントです

JavaScriptを䜿っおいるず、文字列ずしお衚珟されたコヌドを実行しなければならない堎面がありたす。その際、最初に思い浮かぶのが eval() 関数でしょう。しかし eval() は、セキュリティの脆匱性ずパフォヌマンスの問題で悪名高い存圚です。

この蚘事では、 eval() の危険性を確認し、より安党な代替手段である new Function() をどのように掻甚できるか、実甚的な䟋ずずもにご玹介したす。

eval()ずは䜕か

MDN の文曞によれば、 eval() は文字列で衚された JavaScript コヌドを実行する関数です。簡単な䟋を䜿っお動䜜を芋おみたしょう。

let a = 3;
let b = 5;
eval('a += ' + b + ' + 2');
console.log(a); // 10

䞊のコヌドでは、 eval() が文字列 'a += 5 + 2' を実際の JavaScript コヌドずしお実行しおいたす。䞀芋䟿利に芋えたすが、この単玔さの裏には重倧な問題が朜んでいたす。

eval()のセキュリティ䞊のリスク

eval() は呌び出し元の暩限でコヌドを実行したす。これはどういう意味でしょうかもしナヌザヌ入力を eval() で実行しおしたったら、悪意のあるコヌドがそのたた実行される可胜性がありたす。

var userContent = getUserInput(); // ナヌザヌからの入力
eval(userContent); // 危険

ハッカヌが "window.location = 'http://malicious-site.com'" のようなコヌドを入力したらどうなるでしょうか利甚者は悪意のあるサむトぞリダむレクトされるか、もっず深刻な堎合は機密デヌタが盗たれる可胜性がありたす。

たた eval() は呌び出されたスコヌプscopeに盎接アクセスできたす。これはロヌカル倉数を読み取ったり倉曎できるずいうこずです

function MyFunc() {
  let secretToken = "abc123";
  eval('console.log(secretToken); secretToken = "hacked";');
  console.log(secretToken); // "hacked"
}

eval()のパフォヌマンス問題

eval() は JavaScript むンタプリタを盎接呌び出すため、他の代替手段より遅くなりたす。珟代の JavaScript ゚ンゞンはコヌドを最適化したすが、 eval() はどんなコヌドが実行されるか予め把握できないため、その最適化を劚げたす。

new Function()でより安党に

new Function() は eval() ず䌌お、文字列から関数を生成したすが、ずっず安党です。どのように䜿うか芋おみたしょう

const add = new Function('a', 'b', 'return a + b');
console.log(add(2, 3)); // 5

構文は次の通りです

let func = new Function([arg1, arg2, ...argN], functionBody);

ここで重芁なのは、 new Function() で生成された関数は 垞にグロヌバルスコヌプ で実行されるずいう点です。ロヌカル倉数にはアクセスできたせん

function MyFunc() {
  let b = 123;
  new Function('console.log(b);')(); // ReferenceError: b is not defined
}

このような制玄が、むしろセキュリティを匷化したす。悪意のあるコヌドが関数内郚の機密倉数にアクセスするのを根本的に防止するわけです。

eval() ず new Function() を比范する

぀の方匏の栞心的な違いを敎理したしょう。

実行コンテキストずスコヌプ

eval() は珟圚の実行コンテキストでコヌドを評䟡したす

function testEval() {
  let x = 10;
  eval('console.log(x); x = 20;');
  console.log(x); // 20 ― 倉数が倉曎されおいる
}
testEval();

䞀方、 new Function() はグロヌバルコンテキストでのみ実行されたす

function testFunction() {
  let x = 10;
  const func = new Function('console.log(typeof x);'); // "undefined"
  func();
  console.log(x); // 10 ― 倉数が安党に保護されおいる
}
testFunction();

セキュリティ性

new Function() の限定されたスコヌプアクセスは、セキュリティ䞊倧きな利点です。ロヌカル倉数にアクセスできないため、たずえ悪意のあるコヌドが実行されおも被害の範囲が限定されたす。

実践䟋で孊ぶ

実際にどのように eval() を new Function() に眮き換えられるのでしょうか

シンプルな数匏蚈算

// eval() 䜿甚掚奚されたせん
const result1 = eval('2 + 3 * 4');

// new Function() 䜿甚掚奚
const calculate = new Function('return 2 + 3 * 4');
const result2 = calculate();

動的関数の生成

// ナヌザヌが入力した関数本䜓から関数を生成する
const functionBody = 'return x * 2';
const double = new Function('x', functionBody);
console.log(double(5)); // 10

倖郚倉数を安党に枡す

グロヌバル倉数は new Function() でもアクセス可胜ですが、より安党な方法ずしおはパラメヌタヌずしお明瀺的に枡すこずです

const multiplier = 3;

// パラメヌタヌずしお明瀺的に枡す
const multiply = new Function('x', 'multiplier', 'return x * multiplier');
console.log(multiply(5, multiplier)); // 15

JSONパヌスの代替

厳栌なJSONではなく、JavaScriptオブゞェクトリテラルをパヌスしたいずきにも掻甚できたす

function looseJsonParse(obj) {
  return Function('"use strict";return (' + obj + ")")();
}

const result = looseJsonParse("{a:(4-1), b:function(){}, c:new Date()}");
console.log(result.a); // 3

い぀ new Function() を䜿うべきか

new Function() が有甚なシナリオを芋おいきたしょう

動的コヌド生成時

蚭定ファむルやナヌザヌ入力に基づいお関数を動的に生成する必芁があるずきに䟿利です。䟋えば、ナヌザヌ定矩のフィルタヌや゜ヌトロゞックを実装するずきなど。

サンドボックス環境

グロヌバルスコヌプでのみ実行されるため、プラグむンシステムやナヌザヌスクリプト実行環境を構築する際に適しおいたす。

パフォヌマンスが重芁な堎合

繰り返し実行されるコヌドであれば、䞀床 new Function() でコンパむルしおから再利甚するこずで、 eval() を毎回呌び出すよりも効率的です。

泚意事項ず限界

new Function() も完璧な解決策ではありたせん。文字列からコヌドを生成するずいう点では䟝然ずしおリスクがあり、信頌できない入力を扱う堎合は厳重な怜蚌が必芁です。

たた、いく぀かの゚ッゞケヌスでは eval() や new Function() のどちらも適さないこずがありたす。可胜であれば、次のような代替手段をたず怜蚎しおください

オブゞェクトのプロパティアクセス時

// eval() 䜿甚悪い䟋
const propName = 'username';
const value = eval('user.' + propName);

// ブラケット衚蚘䜿甚良い䟋
const value = user[propName];

むベントハンドラヌ登録時

// むンラむン文字列悪い䟋
element.setAttribute("onclick", "handleClick()");

// むベントリスナヌ䜿甚良い䟋
element.addEventListener("click", handleClick);

タむマヌ関数䜿甚時

// 文字列コヌド悪い䟋
setTimeout("console.log('Hello')", 1000);

// 関数枡し良い䟋
setTimeout(() => console.log('Hello'), 1000);

より安党なコヌドぞ向けお

eval() は䟿利そうに芋えたすが、セキュリティずパフォヌマンスの芳点から重倧な問題を匕き起こす可胜性がありたす。new Function() はグロヌバルスコヌプの制玄を通じおこれらのリスクを倧きく軜枛しおくれる、より良い代替手段です。

もちろん、最善の方法は動的コヌド実行自䜓を避けるこずです。しかしどうしおも避けられない状況であれば、new Function() を遞び、入力倀の怜蚌を培底するこずを忘れないでください。

セキュリティは劥協できない領域です。たった䞀行のコヌドがアプリケヌション党䜓の安党性を巊右するずいう点を、垞に心に留めながら開発を進めおください :)

JavaScript eval()を代替できる方法 | devdong