サーバーを通らない「透明な侵入者」?DOMベースXSSの正体と防犯術
こんにちは!セキュリティの現場で日々、複雑な攻撃と向き合っているエンジニアです。
今日は、多くの開発者が「サーバーを通っていないから大丈夫だろう」と油断してしまう、DOMベースXSS(クロスサイトスクリプティング)についてお話しします。これ、実は現代のWebアプリ開発において、非常に巧妙で厄介な「見えない泥棒」なんです。
小難しい専門用語はなるべく控えめに、私たちの日常にある「防犯」に例えて、一緒に紐解いていきましょう。
—
1. DOMベースXSSって、結局どんな「泥棒」なの?
一般的なXSSは、サーバーに悪意のあるコードが保存され、それがブラウザに送られてくる「外からの侵入」です。しかし、DOMベースXSSは違います。
イメージしてください。あなたの家には厳重なセキュリティゲート(サーバー)がありますが、「家の玄関を通らずに、壁の隙間(ブラウザの機能)から勝手に部屋に入り込んでくる泥棒」がいたらどうでしょう?
ブラウザ上で動くJavaScriptが、URLの一部(例えば location.hash など)を「深く考えずにそのまま画面に表示してしまう」とき、その隙間が生まれます。
例えばこんなコード…(危険な例)
// URLの # 以降をそのままページ内に書き出してしまう危険な処理
const hash = location.hash.substring(1);
document.getElementById(‘welcome-message’).innerHTML = “こんにちは、” + hash + “さん!”;
このコードだと、URLの末尾に のような罠を仕込まれると、ブラウザは「ああ、これはただの名前だな」と信じ込んでしまい、悪意あるプログラムを実行してしまいます。これがDOMベースXSSのメカニズムです。
—
2. 「鍵」の掛け方:防御の基本原則
この泥棒を防ぐには、「ブラウザに渡すものは、すべて疑え」という原則を徹底することです。
対策①:innerHTMLは使わない(「窓」を塞ぐ)
innerHTML は、渡された文字列を「ただの文字」ではなく「HTMLの構造」として解釈しようとします。これが最大の間違いです。代わりに、文字として安全に扱う textContent を使いましょう。
// 安全な書き方:これならタグを書いても「ただの文字」として表示されるだけです
const hash = location.hash.substring(1);
document.getElementById(‘welcome-message’).textContent = “こんにちは、” + hash + “さん!”;
これだけで、泥棒が窓から入ってきても「壁紙に落書き」するくらいのことしかできなくなります。
—
3. 「防犯カメラ」と「警備システム」:CSPの導入
コードの書き換えが難しい場合や、二重の防犯対策として強力なのがCSP(Content Security Policy)です。これは、Webサイトが「どのスクリプトを信頼するか」をブラウザに伝えるための防犯システムです。
サーバーのレスポンスヘッダーに、以下のような設定を追加してみてください。
Content-Security-Policy: default-src ‘self’; script-src ‘self’; object-src ‘none’;
default-src 'self': 基本的に、自分のサイト以外から持ってきたデータは信用しません。script-src 'self': 外部の怪しいスクリプトの実行をブロックします。object-src 'none': 悪用されやすいプラグインの機能を完全にオフにします。
これを設定しておくと、仮にコードに隙間があっても、ブラウザが「おっと、このスクリプトは許可リストにないぞ!」と察知して、実行を止めてくれます。
—
4. 最後に:セキュリティは「終わりのない旅」
DOMベースXSSを防ぐためのポイントをまとめます。
1. URLなどの外部入力は、決して信頼してそのまま出力しない。
2. innerHTML や document.write は極力避け、textContent を使う。
3. CSPを導入し、ブラウザという「警備員」を雇う。
セキュリティと聞くと身構えてしまうかもしれませんが、結局のところ「自分の書いたコードが、誰に、どう悪用される可能性があるか」を想像する力、これに尽きます。
今日からあなたのコードでも、location.hash や location.search を扱っている場所がないか、ぜひチェックしてみてください。一歩ずつの積み重ねが、あなたの開発するサービスを、そしてユーザーを守る一番の盾になります。
それでは、また次の現場でお会いしましょう!応援しています!
コメント