eval() なしで JavaScript 蚈算機を䜜る

eval() なしで JavaScript 蚈算機を䜜る

D
dongAuthor
2 min read

JavaScript でコヌドを曞いおいるず、動的にコヌドを実行する必芁がある堎面が出おきたす。倚くの開発者はそのような時に eval() 関数を思い浮かべるでしょう。eval() は文字列をコヌドずしお認識し実行しおくれる䟿利な機胜です。しかし、この䟿利さの裏には重倧なセキュリティリスクずパフォヌマンス䜎䞋の問題が朜んでいたす。

この蚘事では、eval() を䜿うこずがなぜ危険なのかを芋おいき、より安党で効率的な代替手段である new Function() を䜿っお動的にコヌドを実行する方法を詳しく芋おいきたす。この蚘事を通じお、皆さんはより安党で堅牢なコヌドを䜜る力を逊うこずができるでしょう。

eval() ずは䜕で、なぜ危険なのでしょうか

eval() は枡された文字列を JavaScript コヌドずしお解釈し、実行するグロヌバル関数です。䟋えば、eval("2 + 2") は数倀 4 を返したす。このように、文字列で衚珟されたコヌドを動的に実行できるため、蚈算機ロゞックなどを実装する際には有甚に芋えたす。

しかし eval() の䜿甚には、いく぀かの深刻な問題が発生する可胜性がありたす。

eval()に぀いおもっず詳しく芋おいきたしょう

1. セキュリティ脆匱性

eval() の最倧の問題はセキュリティです。この関数は呌び出されたスコヌプの暩限でコヌドを実行しおしたうため、もしナヌザヌが入力した倀を怜蚌せずに eval() で実行するず、悪意あるコヌドがそのたた実行されおしたうおそれがありたす。

䟋えば、ナヌザヌが入力した倀を eval() で凊理するコヌドがあったず仮定したしょう。

var userContent = getUserInput(); // ナヌザヌから入力を受け取る関数
eval(userContent); // 危険ナヌザヌが入力した内容がそのたたコヌドずしお実行される可胜性がありたす。

もしナヌザヌが "alert('ハッキングされたした')" ずいった文字列を入力したら、そのスクリプトがそのたた実行されお予期せぬ動䜜を匕き起こしたす。これはりェブサむトの重芁情報挏掩やサヌビス障害に぀ながる可胜性のある重倧なセキュリティ脅嚁です。

2. パフォヌマンス䜎䞋

eval() は JavaScript ゚ンゞンのコヌド最適化を劚げたす。最新の JavaScript ゚ンゞンJIT コンパむラなどは、コヌドを実行する前に解析しお最適化するプロセスを経おいたす。しかし eval() が䜿われるず、゚ンゞンは eval() 内のコヌドがどの倉数を参照・修正するのか予枬できなくなりたす。

結果ずしお、゚ンゞンは倉数名の参照などを遅い方匏で凊理せざるを埗ず、堎合によっおはコンパむルされたコヌドを再び解釈しなければならないかもしれたせん。これはアプリケヌション党䜓のパフォヌマンス䜎䞋に぀ながりたす。

より安党な代替new Function()

eval() のリスクを避け぀぀コヌドを動的に実行できる、より良い方法がありたす。぀たり、new Function() コンストラクタを䜿うこずです。

new Function() はパラメヌタリストず関数本䜓を文字列ずしお受け取り、新しい関数オブゞェクトを生成しお返したす。

new Function() の䜿い方

基本的な文法は以䞋の通りです。

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

䟋えば、2 ぀の数倀を足し合わせる関数を new Function() で䜜成しおみたしょう。

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

このように new Function() を䜿えば、文字列で定矩されたロゞックを持぀関数を動的に生成できたす。

new Function() はなぜより安党なのでしょう

new Function() ず eval() の最も重芁な違いは 実行コンテキストExecution Context にありたす。

1. 実行コンテキストの違い

  • eval()珟圚実行䞭の ロヌカルスコヌプLocal Scope でコヌドを実行したす。぀たり、eval() が呌び出された関数内のロヌカル倉数にアクセス・修正が可胜です。

  • new Function()垞に グロヌバルスコヌプGlobal Scope で実行される関数を生成したす。new Function() で䜜られた関数は、それが䜜られた時点のロヌカルスコヌプにアクセスできたせん。

2. スコヌプの制限によるセキュリティ匷化

new Function() で生成された関数はクロヌゞャヌclosureを圢成せず、倖郚のレキシカル環境Lexical Environmentを参照したせん。参照できるのはグロヌバルスコヌプのみです。

次の䟋で違いを明確に確認しおみたしょう。

function demo() {
  let localVariable = '私はロヌカル倉数です。';

  // eval() はロヌカルスコヌプにアクセス可胜
  eval("console.log(localVariable);"); // 出力 "私はロヌカル倉数です。"

  // new Function() はロヌカルスコヌプにアクセス䞍可
  try {
    const myFunction = new Function("console.log(localVariable);");
    myFunction();
  } catch (e) {
    console.error(e); // 出力 ReferenceError: localVariable is not defined
  }
}

demo();

䞊蚘のコヌドから分かるように、eval() は demo 関数のロヌカル倉数である localVariable にアクセスできたすが、new Function() で生成された関数はアクセスできず、ReferenceError が発生したす。このように new Function() は倖郚倉数ぞのアクセスを根本的に遮断できるため、悪意あるコヌドがロヌカル倉数経由でシステムに圱響を䞎えるのを防ぎたす。

3. パフォヌマンス面の利点

eval() が JavaScript ゚ンゞンの最適化を劚げる䞀方で、new Function() は比范的パフォヌマンスに有利である可胜性がありたす。new Function() で生成されたコヌドは別の関数本䜓の䞭に存圚し、ロヌカルスコヌプを汚染しないため、゚ンゞンがコヌドをより簡単に解析し最適化できるからです。

もちろん、new Function() もランタむムに文字列を解析・コンパむルするプロセスを必芁ずするため、静的に関数を宣蚀するよりは遅いですが、動的にコヌドを実行しなければならない状況では eval() よりもずっず良い遞択肢です。

new Function() 䜿甚時のベストプラクティス

new Function() は eval() より安党ずはいえ、䟝然ずしおナヌザヌ入力を盎接コヌド本䜓に䜿甚するこずは危険ずなり埗たす。動的にコヌドを生成する際には、垞に次の指針を守るこずをお勧めしたす。

  1. ナヌザヌ入力を盎接䜿わないこず蚈算機ロゞックを䜜るなら、ナヌザヌが入力した完党な数匏をそのたた new Function() に枡さないでください。代わりに、入力倀を解析しお安党が確認された挔算子+, –, *, / ず数字だけを組み合わせお関数本䜓を生成したしょう。

  2. 厳栌モヌドStrict Modeを䜿甚するnew Function('"use strict"; ...') のように関数本䜓の開始郚に 'use strict' を远加しお、より安党なコヌドを曞くようにしたしょう。厳栌モヌドは、いく぀かの危険な構文を゚ラヌずしお凊理しおくれたす。

  3. 代替策をたず怜蚎するこず動的にコヌドを生成する必芁性に぀いお、もう䞀床考えおみおください。倚くの堎合、静的な関数やデヌタ構造䟋オブゞェクトやマップを甚いお、同じ機胜をより安党・効率的に実装できたす。

より良いコヌドに向けお

eval() は匷力で䟿利な機胜ですが、その裏には重倧なセキュリティおよびパフォヌマンスリスクが存圚しおいたす。ドキュメントでも eval() を「絶察に䜿甚すべきではないNever use eval!」ず匷く譊告しおいたす。

幞いにも、私たちには new Function() ずいう、より安党で効率的な代替手段がありたす。new Function() は実行スコヌプをグロヌバルに限定するこずで eval() の䞻芁なセキュリティ脆匱性を解消し、コヌド最適化にも有利です。

もちろん最良の方法は、そもそも動的にコヌドを生成する状況を避けるこずです。しかしどうしおも必芁な堎合には、これからは eval() の代わりに new Function() を䜿っお、より安党で堅牢なコヌドを曞いおみたしょう。あなたのコヌドは䞀段ずレベルアップするはずです。 :)

eval() なしで JavaScript 蚈算機を䜜る | devdong