DOMベースXSS:その「便利さ」が招く、ブラウザの中の泥棒にご用心
こんにちは。セキュリティの現場で日々、複雑な攻撃と格闘しているエンジニアです。
今日は、Web開発の現場で「意外と見落とされがちな落とし穴」である「DOMベースXSS(クロスサイトスクリプティング)」についてお話ししましょう。
「XSSってサーバーが攻撃されるやつじゃないの?」と思っているなら要注意。DOMベースXSSは、サーバーを一切経由せず、あなたのブラウザの中で完結してしまう「ステルス型」の脆弱性なんです。
—
1. 例え話で理解する「DOMベースXSS」
まずは、身近な例えで考えてみましょう。
想像してください。あなたは自分の家に住んでいて、玄関(サーバー)にはしっかりとした鍵をかけています。しかし、家の奥にある「メモ書き用のホワイトボード(DOM)」に、誰かが通りすがりに「勝手に伝言を書ける隙間」があったとしたらどうでしょう?
- 泥棒(攻撃者):あなたの家(ブラウザ)の中に侵入し、ホワイトボードに「この家の金庫の番号は〇〇です」なんて嘘の情報を書いたり、家族を騙すようなメッセージを残したりします。
- 被害者(あなた):ホワイトボードに書かれた文字を信じてしまい、大切な情報を漏らしてしまいます。
DOMベースXSSは、まさにこれと同じです。Webページが「URLの末尾(住所の一部)」などを確認せずにそのまま画面に表示してしまうと、悪意ある人がそこに「偽の命令」を忍び込ませることができるのです。
—
2. なぜ起こる?「ソース」と「シンク」の関係
専門用語が少し出ますが、怖がる必要はありません。この2つの言葉だけ覚えてください。
- ソース(Source):外部からやってくる「データ」の入り口。(例:
location.hash=URLの#以降の文字列など) - シンク(Sink):データをブラウザが「表示・実行」してしまう場所。(例:
innerHTML=HTMLとして中身を書き換える命令など)
攻撃者は、「ソース(怪しいデータ)」を「シンク(危険な実行場所)」に橋渡しさせることで、ブラウザ上で勝手なプログラムを動かします。
危険なコードの例
以下は、URLの末尾(#以降)をそのまま画面に表示してしまう、ちょっと危ないコードです。
// URLの # の後ろを取得する(これがソース!)
var userMessage = decodeURIComponent(window.location.hash.substring(1));
// 画面のdiv要素にそのまま放り込む(これがシンク!)
// もし userMessage に “” が入っていたら…?
document.getElementById(‘display-area’).innerHTML = userMessage;
このページに https://example.com/# とアクセスさせると、ブラウザは「画像を表示しようとしたけど失敗した!じゃあエラー処理としてalert(警告)を実行しよう!」と騙されてしまいます。これが攻撃の仕組みです。
—
3. どうやって防ぐ?「二重の防犯対策」
では、どうすればこの「泥棒」を防げるのでしょうか。対策は大きく分けて2つあります。
対策①:危ない「シンク」を使わない(基本の対策)
innerHTML は、渡された文字列を「HTML」として解釈しようとするため、非常に強力で危険です。「ただの文字」として表示したいだけなら、別の方法を使いましょう。
// 修正版:innerHTMLではなく、textContentを使う
// これなら、仮にスクリプトが混じっていても「ただの文字列」として表示されるだけです!
document.getElementById(‘display-area’).textContent = userMessage;
textContent を使えば、ブラウザはそれをプログラムではなく「ただの文章」として扱うため、攻撃は不発に終わります。
対策②:防犯カメラを設置する(CSPの導入)
もしコードに不備があっても、ブラウザに「怪しい命令は無視せよ」と指示するCSP(Content Security Policy)という仕組みが有効です。HTTPレスポンスヘッダーに以下のような設定を追加しましょう。
HTTPヘッダーの例
「信頼できるソース以外からのスクリプト実行は一切禁止!」とブラウザに伝えます
Content-Security-Policy: default-src ‘self’; script-src ‘self’;
これをしておくだけで、仮に攻撃者がコードを埋め込めたとしても、ブラウザが「このスクリプトは許可されていません!」とブロックしてくれます。
—
最後に:セキュリティは「疑うこと」から始まる
DOMベースXSSは、開発者が「便利にデータを扱いたい」と思った瞬間に、その隙を突いてやってきます。
1. 「ユーザーからのデータは、すべて嘘かもしれない」と疑うこと。
2. 「innerHTML」を安易に使わず、より安全な「textContent」を選ぶこと。
3. 「CSP」という強力な防犯カメラを設置しておくこと。
これらを守るだけで、あなたの作るWebサイトは格段に安全になります。最初は難しく感じるかもしれませんが、まずは「URLの情報をそのまま表示していないかな?」とコードを見直すことから始めてみてください。
一歩ずつ、安全な開発者への道を歩んでいきましょう!応援していますよ。
コメント