「魔法の関数」という名の時限爆弾:eval()とsetTimeout()が招くXSSの深淵
現場でコードレビューをしていると、今でもたまに遭遇するんだ。「動的に処理を切り替えたいから」という理由で、平然とeval()を使っているコードに。
正直に言おう。eval()は、セキュリティの観点から言えば「悪魔との契約」だ。
多くのエンジニアが「XSS(クロスサイトスクリプティング)」と聞くと、タグの注入を想像する。だが、実務で遭遇する最も厄介で、かつ「防御がすり抜ける」脆弱性は、こうした実行関数にユーザー入力を直接流し込むケースだ。今回は、この盲点について、現場の知見を交えて徹底的に解説する。
---
1. なぜeval()やsetTimeout()が危険なのか
これらは「文字列をコードとして解釈・実行する」関数だ。もし、攻撃者が送り込んだ文字列がこの関数に到達したら? 攻撃者は、ブラウザの実行コンテキストを完全に掌握できる。
攻撃シナリオ:DOMベースXSSの罠
例えば、URLのクエリパラメータから設定を受け取り、setTimeoutで遅延処理を行うようなWebアプリを想像してほしい。
// 脆弱な実装例
const userParam = new URLSearchParams(window.location.search).get("callback");
// 攻撃者が ?callback=alert(document.cookie) と送り込んだら即死する
setTimeout(userParam, 1000);
このコードを見た攻撃者は笑うだろう。eval()も同様だ。サーバーサイドで生成したJSON文字列を、そのままeval()でパースしようとするレガシーなコードを見かけるが、これも同様に攻撃者の格好の餌食だ。
---
2. 撲滅のためのベストプラクティス:3つの鉄則
現場で戦う我々は、以下の原則をコード規約(lint)に叩き込むべきだ。
① eval()を禁止し、JSON.parse()へ置き換える
文字列をオブジェクトに変換したいだけなら、JSON.parse()一択だ。これはデータのみを解析し、コードを実行しない。
② setTimeout / setInterval には「関数参照」を渡す
文字列を渡すのは絶対にNGだ。必ず定義済みの関数名、または無名関数(アロー関数)を渡すこと。
③ CSP(コンテンツセキュリティポリシー)で根絶する
コードが混入してしまったとしても、実行を許可しない設定が最後の砦になる。
---
3. セキュアな実装への書き換え(実務サンプル)
NGコード
// 最悪のコード:文字列をそのまま実行
eval("doSomething('" + userInput + "')");
setTimeout("alert('" + userInput + "')", 1000);
OKコード(推奨実装)
// 1. JSONパースは専用関数を使う
const data = JSON.parse(jsonString);
// 2. 関数参照を使用する
function showNotification(msg) {
console.log(msg);
}
// ユーザー入力を処理する場合でも、ロジックを関数内に閉じ込める
setTimeout(() => {
showNotification(userInput);
}, 1000);
---
4. インフラ側で「最後の防衛線」を張る(CSP設定)
どんなに気をつけていても、ヒューマンエラーはゼロにはできない。だからこそ、サーバー側からのガードが重要だ。NginxやWebサーバーのヘッダーで、unsafe-evalを禁止するCSPを強制しよう。
Nginxの設定例:
危険なevalやインラインスクリプトをブラウザ側で実行させない
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none';";
これを導入するだけで、万が一どこかでeval()が走っても、ブラウザがコンソールに強力なエラーを吐き出して実行をブロックしてくれる。
---
5. 最後に:エンジニアとしてのマインドセット
「動的な処理を簡単に書ける」という誘惑は、技術的負債であり、セキュリティ上の最大のリスクだ。
もし君がチームリーダーなら、コードレビューでeval、setTimeout(string), setInterval(string), new Function(string)を見つけたら、「なぜその実装が必要なのか」を問うのではなく、「なぜその実装を即刻削除しないのか」を問い詰めてほしい。
セキュリティは「教科書を暗記すること」ではない。「攻撃者の視点に立ち、どこに脆弱性の種が埋め込まれているかを先回りして摘み取ること」だ。
現場で泥臭く戦うエンジニアこそが、最強のセキュリティガードだ。明日からの開発で、一度自分のコードの「実行関数」を眺めてみてくれ。そこに、まだ見ぬ脆弱性が潜んでいるかもしれないからな。
コメント