「eval()は悪魔の扉」― 動的コード実行が引き起こす惨劇と、明日から捨てるべき悪しき習慣
現場でコードレビューをしていると、未だに eval() を使った実装に出くわすことがある。「動的に処理を切り替えたい」「JSONのパースが面倒だったから」――理由は様々だが、セキュリティの観点から言えば、それは「自分の家の鍵を道端に落として歩く」のと同じくらい無防備な行為だ。
今日は、なぜ eval() や setTimeout() への文字列渡しが、システムを崩壊させる「特等席」を提供してしまうのか、そのメカニズムと現場レベルの防御策を叩き込む。
—
1. なぜ「動的コード実行」が命取りになるのか
eval() は、渡された文字列をそのままJavaScriptエンジン(あるいはPHP等のインタープリタ)に「コード」として解釈・実行させる機能だ。もし、この文字列の中にユーザーからの入力が混じっていたらどうなるか。
攻撃シナリオ(PoC)
例えば、URLパラメータから値を受け取り、それを setTimeout で処理するこんなコードがあったとする。
// 脆弱な実装例:絶対にやってはいけない
const params = new URLSearchParams(window.location.search);
const callback = params.get(“callback”);
// ユーザーが ?callback=alert(‘Hacked!’) と入力すると…
setTimeout(callback, 1000);
攻撃者はこれを見て「お、DOMの操作権限を奪えるな」と即座に判断する。alert() 程度なら可愛いものだが、実際には fetch() を使ってログイン中のセッションCookieを外部サーバーに送信する、あるいはフォームを改ざんしてクレジットカード情報を抜き取るコードが注入される。
「動的」という甘美な響きは、攻撃者にとって「自由」を意味する。 サーバーサイドの eval() であれば、リモートコード実行(RCE)に直結し、そのサーバーは即座にボットネットの一員となるだろう。
—
2. 安全な代替案:ルールを変える
「動的に処理を切り替えたい」というニーズの99%は、eval() を使わずに解決できる。
A. JSONデータの処理には JSON.parse()
古のJavaScriptでは eval() を使ってJSONをパースしていた時代もあったが、今は違う。JSON.parse() を使えば、データはあくまで「データ」としてのみ扱われ、コードとして実行されるリスクはゼロだ。
// 悪い例: eval(‘(‘ + jsonString + ‘)’)
// 良い例:
try {
const data = JSON.parse(jsonString);
console.log(data.username);
} catch (e) {
console.error(“不正なJSON形式です”, e);
}
B. 関数呼び出しは「マッピング」で解決
eval() で関数名を動的に呼び出そうとするのは悪手だ。代わりに「名前と関数の辞書(オブジェクト)」を作れ。
// セキュアな処理の切り替え実装
const actions = {
“save”: () => console.log(“保存処理を実行”),
“delete”: () => console.log(“削除処理を実行”)
};
const actionName = “save”; // ユーザー入力など
if (actions[actionName]) {
actions[actionName](); // 安全に呼び出せる
} else {
console.error(“許可されていない操作です”);
}
—
3. インフラ層での「ダメ押し」:CSP(Content Security Policy)
コードレベルで完璧に排除するのが原則だが、人間はミスをする。万が一の脆弱性混入に備え、ブラウザ側で「evalを動かさせない」という強制力を持たせるのが、プロの防御だ。
Nginxの設定、あるいはメタタグで CSP を設定し、'unsafe-eval' を徹底的に排除する。
Nginx設定例:evalを徹底拒否するヘッダー
add_header Content-Security-Policy “default-src ‘self’; script-src ‘self’; object-src ‘none’;”;
default-src 'self': 基本的に自身のドメイン以外からの読み込みを禁止。script-src 'self': インラインスクリプトやeval()を禁止。object-src 'none': Flash等のプラグイン脆弱性を封じ込める。
—
最後に:セキュリティは「疑う」ことから始まる
私が現場で数々のインシデントを見てきて確信していることがある。それは、「自分は大丈夫」という思い込みが一番の脆弱性であるということだ。
「この変数なら大丈夫だろう」「このライブラリは有名だから平気だろう」。そう思った瞬間に、攻撃者はその隙間をこじ開けてくる。
今日紹介した手法は、いわば「基本のキ」だ。eval() をコードから一掃し、ロジックをマッピングで管理する。たったこれだけの設計変更で、あなたのシステムは格段に堅牢になる。
コードは、誰かが悪意を持って触ることを前提に書く。それが、プロのエンジニアとしての最低限のたしなみだ。次回のコードレビューで、もし誰かが eval() を使っていたら、迷わずその手を止めさせろ。それが、あなたのシステムと、その先のユーザーを守る第一歩になる。
コメント