【実務・中級編】JavaScriptのeval()やsetTimeout(string)の危険性と代替案 – アプリケーションセキュリティ & 安全な開発防御ガイド

悪魔の招待状:JavaScriptでeval()を使ってはいけない「本当の理由」と現場の防衛術

「とりあえず動けばいいや」で書いた数行のコードが、数年後に会社を傾ける脆弱性の温床になる。セキュリティの現場で何度も見てきた光景だ。

今日取り上げるのは、初心者が犯しがちで、かつ中級者が油断して踏み抜く地雷。eval() を筆頭とする「文字列をコードとして実行する関数群」だ。なぜこれらが現代のWeb開発において「死刑宣告」に等しいのか、そしてどう代替すべきか。プロの視点で徹底解説する。

—

1. なぜ eval() が「悪魔の関数」なのか

eval()、setTimeout(string)、setInterval(string)、new Function(string)。これらは全て、「外部からの入力(文字列)を、そのままプログラムの実行権限で動かす」という特性を持つ。

これがなぜ危険か? 答えはシンプルだ。「実行されるコードの制作者」が、あなたではなく「攻撃者」になる可能性があるからだ。

攻撃者の視点:PoC(概念実証)の恐怖

例えば、ユーザーがプロフィール画面で入力した名前を、動的に表示する処理を考えてみよう。

// 脆弱な実装例
const userInput = “alert(document.cookie)”; // 攻撃者が入力した値
eval(“console.log(‘Hello, ” + userInput + “‘)”);

これだけで、ユーザーのセッションCookieは即座に外部の攻撃サーバーへ送信される。eval() は現在のスコープで実行されるため、そのWebページが持つあらゆる権限を乗っ取ることができる。OSコマンドインジェクションのJavaScript版と言えば、その恐ろしさが伝わるだろうか。

—

2. 安全な代替案:なぜ「評価」ではなく「参照」を使うのか

現代のセキュアな開発において、文字列をコードとして実行する必要はほぼ存在しない。もしあるとすれば、それは設計が間違っている証拠だ。

代替パターン1:JSONのパース

外部からのデータをオブジェクトとして扱いたいなら、eval() ではなく必ず JSON.parse() を使う。これは文字列を「データ」としてしか扱わないため、悪意のあるコードが含まれていても、ただの文字列として処理が止まるだけだ。

// 安全なデータ処理
const jsonString = ‘{“name”: “Alice”, “role”: “admin”}’;
const data = JSON.parse(jsonString); // 確実にデータとして扱う
console.log(data.name);

代替パターン2:関数参照とルックアップテーブル

「文字列から関数を呼び出したい」というケースもよくある。この場合、eval() を使うのではなく、オブジェクトを辞書(ルックアップテーブル)として定義するのが鉄則だ。

// 安全な関数呼び出しのパターン
const actions = {
“save”: () => { console.log(“保存しました”); },
“delete”: () => { console.log(“削除しました”); }
};

const command = “save”; // 外部から来た入力値

if (actions[command]) {
actions[command](); // 安全に関数を実行
} else {
console.error(“無効なコマンドです”);
}

—

3. 実務で「封じ込める」ための防御レイヤー

コードレベルでの修正に加え、万が一の脆弱性混入を抑え込む「多層防御」の設定も紹介しておく。これらは今すぐ本番環境の設定ファイルに組み込むべきだ。

CSP (Content Security Policy) で実行を制限する

HTTPレスポンスヘッダーでCSPを設定すれば、仮に攻撃者がコードを埋め込んでも、ブラウザ側で実行を拒否させることができる。unsafe-eval を許可しない設定がデフォルトだ。

Nginx設定例:

CSP設定:evalやインラインスクリプトを禁止する
add_header Content-Security-Policy “default-src ‘self’; script-src ‘self’; object-src ‘none’;”;

サーバーサイド(Node.js/Python/PHP)での教訓

JavaScriptだけでなく、バックエンドでも eval() や exec() の類は避けるべきだ。もしPHPで eval() を使っているコードを見つけたら、それはリファクタリングの優先度を「緊急」にすべきだ。

PHPでの悪い例:

// 絶対にやるな:外部入力をそのまま実行
eval($_GET[‘code’]);

PHPでの対策(設計の見直し):
PHPの場合は、call_user_func() を使う場合でも、必ずホワイトリストによる制限を設けること。

$allowed_methods = [‘get_user’, ‘get_post’];
$method = $_GET[‘action’];

if (in_array($method, $allowed_methods)) {
call_user_func($method);
}

—

最後に:プロフェッショナルとして

「便利だから」「昔からこうしていたから」という理由は、脆弱性に対する言い訳にはならない。

コードを記述する際、一瞬だけ立ち止まって自分に問いかけてほしい。
「この文字列は、誰が生成したのか?」
「この文字列をコードとして解釈させる必要は、本当にあるのか?」

あなたが書くその1行が、ユーザーの個人情報を守る壁になる。あるいは、崩壊させる亀裂になる。この違いを常に意識できるエンジニアこそが、信頼される技術者であると私は信じている。

もし、プロジェクト内にまだ eval() が残っているなら、今すぐ修正チケットを切るべきだ。それが、君のシステムを守るための第一歩だ。

コメント

タイトルとURLをコピーしました