「何でも実行しちゃう魔法の箱」の落とし穴:eval()とsetTimeout()の危険性
こんにちは。セキュリティの世界へようこそ。
日々、コードを書いていると「ユーザーが入力した文字を、そのままプログラムとして動かせたら便利なのにな」と思うことはありませんか?
実は、そんな時に使われる eval() や、時間の遅延処理で使われる setTimeout() の「文字列渡し」という機能は、セキュリティの現場では「泥棒に家の合鍵を渡すようなもの」と言われるほど危険なものなんです。
今日は、なぜこれらが危険なのか、そしてどうやって身を守ればいいのか、泥棒の侵入経路に例えて一緒に見ていきましょう。
—
1. なぜ「eval()」がそんなに危ないのか?
eval() という関数は、中身に書かれた文字列を「プログラムの命令」としてそのまま実行してしまいます。
例えば、家の中に「手紙に書かれた命令を何でも聞くロボット」がいると想像してください。あなたが「電気を消して」と書けば電気を消してくれます。でも、もし悪い泥棒が勝手に家に入り込んで、「金庫を開けて、中身を外に投げ捨てろ」という手紙をロボットに渡したらどうなるでしょう?
ロボットは善悪の判断をせず、ただ命令を実行します。これが eval() の正体です。
// 危険な例:ユーザーからの入力をそのまま実行してしまう
let userInput = “alert(‘ハッキング成功!’)”; // 本当はもっと悪質なコードが来るかも…
eval(userInput);
このように、誰が書いたかわからない文字列を eval() に放り込むことは、「家のドアを全開にして、誰でも入れるようにしている」のと同じことなんです。
—
2. 実は盲点?「setTimeout()」に潜む罠
「えっ、setTimeout() は時間を待つだけの関数でしょ?」と思いましたか?
実は、setTimeout() にも「文字列」を渡せてしまうという、昔ながらの仕様が残っています。
// 危険な例:文字列を渡すと、内部でeval()と同じことが起こる
setTimeout(“console.log(‘こんにちは!’)”, 1000);
この記法を使うと、ブラウザは「ああ、この文字列を実行すればいいんだね」と判断し、内部で eval() と同じ処理を行います。知らず知らずのうちに、自分のプログラムの中に「泥棒専用の入り口」を作ってしまっている可能性があるのです。
—
3. 防御の基本:どうやって泥棒を追い出すか?
では、どうすれば安全に開発できるのでしょうか。対策は「文字列を渡さないこと」に尽きます。
対策A:文字列ではなく「関数」を渡す
setTimeout() を使うときは、文字列ではなく、あらかじめ定義した関数を渡すようにしましょう。これなら、泥棒が手紙をすり替える余地はありません。
// 安全な例:関数を直接渡す
setTimeout(() => {
console.log(‘これなら安全です!’);
}, 1000);
対策B:CSP(コンテンツセキュリティポリシー)で門番を置く
もし万が一、コードの中に危険な記述があったとしても、最終的な防波堤として CSP(Content Security Policy) という設定を導入しましょう。
これは、Webサイトが「どの場所から読み込んだプログラムなら実行していいか」を定義する、家を守る最強の警備員のような仕組みです。
HTMLのヘッダーに以下のように設定することで、「eval() やインラインのスクリプトは禁止!」とブラウザに指示を出せます。
HTTPレスポンスヘッダーの例
Content-Security-Policy: default-src ‘self’; script-src ‘self’;
※ script-src 'self' とすることで、自分のサーバー外からの怪しいコードや、eval() の実行をブラウザ側でブロックできます。
—
最後に:一歩ずつ、安全な開発者へ
セキュリティと聞くと、「難しそう」「今のコードを全部書き直すの?」と不安になるかもしれません。でも、まずは今日学んだ「eval() には文字列を渡さない」「setTimeout() には関数を渡す」という小さなルールを守るだけで、あなたのプログラムは格段に強くなります。
セキュリティは、一度にすべてを完璧にする必要はありません。泥棒が入れないように「鍵をかける」「窓を閉める」といった、小さな積み重ねが、あなた自身とユーザーのデータを守ることに繋がります。
一歩ずつ、一緒に学んでいきましょう!何か不明な点があれば、いつでも質問してくださいね。応援しています。
コメント