「あなたのサイトが乗っ取られる?」XSSの正体と、今日からできる防犯対策
こんにちは。セキュリティの現場で日々、泥臭いインシデントと向き合っているエンジニアです。
皆さんは「XSS(クロスサイトスクリプティング)」という言葉を聞いて、どんなイメージを持ちますか? 教科書には「悪意のあるスクリプトを注入する」なんて書かれていますが、これだと少し硬いですよね。
今日は、私たちのWebサイトを「一軒の家」に見立てて、泥棒(攻撃者)がどうやって入り込み、何をしていくのか、そして私たちがどうやって鍵をかけるべきか、一緒に紐解いていきましょう。
—
XSSって結局なに?:泥棒の侵入経路を知ろう
XSSは、一言で言えば「あなたのWebサイトを『信頼できる場所』と勘違いさせ、ユーザーのブラウザ上で勝手に悪事を働かせる攻撃」です。
大きく分けて3つのパターンがあります。家の防犯に例えてみましょう。
1. 反射型XSS(通りすがりの置き引き)
ユーザーが検索窓などに打ち込んだ文字が、そのまま画面に表示される仕組みを利用します。「検索結果:『』は見つかりませんでした」というふうに、入力した内容がそのまま画面に「反射」して返ってくるのを利用する手口です。URLに悪意のあるコードを仕込んで誰かに踏ませる、まさに「置き引き」のような罠です。
2. 蓄積型XSS(留守番電話に毒を仕込む)
掲示板やプロフィール欄など、一度書き込んだ内容がサーバーに保存され、後から他のユーザーが見る場所で発生します。誰かが掲示板に悪意あるコードを書き込むと、そこを見た人全員が被害に遭います。家で言えば、共有の伝言板に「毒入りの手紙」を貼っておくような、極めて危険な状態です。
3. DOMベースXSS(家の鍵穴をすり替える)
これが今回深掘りしたい「最新の曲者」です。サーバーを介さず、ブラウザの中だけで完結するのが特徴です。
例えば、URLの末尾(#以降など)にある情報をJavaScriptで読み取って画面に表示するようなサイトだと、サーバー側では攻撃に気づけません。ブラウザの「中身(DOM)」が書き換えられてしまうため、サーバーという玄関を通らずに、勝手に家の鍵穴をすり替えられるようなイメージです。
—
DOMベースXSSの「盲点」:JavaScriptの甘い罠
DOMベースXSSが厄介なのは、「サーバー側では何も悪いことが起きていないように見える」点です。
例えば、以下のようなJavaScriptがあったとします。
// URLのパラメータから名前を取得して表示するコード
const params = new URLSearchParams(window.location.search);
const name = params.get(“name”);
// ここが危険!入力値をそのままHTMLとして書き込んでしまう
document.getElementById(“welcome-msg”).innerHTML = “ようこそ、” + name + “さん!”;
もし攻撃者がURLを ?name= のように書き換えたらどうなるでしょう? ブラウザは「あ、タグを表示しなきゃ!」と頑張りますが、画像が存在しないのでエラー(onerror)が発生し、その瞬間に攻撃者のプログラムが実行されてしまいます。
対策の基本は「innerHTMLを使わないこと」です。
// 安全な書き方:テキストとしてのみ扱う
document.getElementById(“welcome-msg”).textContent = “ようこそ、” + name + “さん!”;
textContent を使えば、たとえ攻撃者がタグを送り込んでも、ただの「文字」として表示されるだけです。泥棒が壁に落書きしようとしても、ただの紙を貼るしかできないのと同じですね。
—
現場で役立つ「最強の防犯装置」:セキュリティヘッダー
コードを直すのと並行して、Webサーバー側で「門番」を立たせることも重要です。それがContent Security Policy (CSP) です。
サーバーからブラウザに向けて、「うちのサイトでは、こういうスクリプトしか動かしてはいけないよ!」という命令を出す設定です。
HTTPレスポンスヘッダーの例
Content-Security-Policy: default-src ‘self’; script-src ‘self’;
- default-src ‘self’: 基本的に、自分のサイト(ドメイン)にあるもの以外は読み込ませない。
- script-src ‘self’: 外部からの怪しいスクリプト実行を禁止する。
これを入れておくだけで、万が一どこかに脆弱性が残っていても、攻撃者が用意した外部の悪意あるサーバーへの通信をブラウザが遮断してくれます。
—
最後に:完璧なセキュリティなんてない
セキュリティの世界には「絶対」はありません。泥棒も常に新しい手口を考えています。
でも、「ユーザーから受け取ったものを、そのまま画面に出さない(出力エスケープ)」「怪しい外部スクリプトを許可しない(CSP)」という二つの鍵をかけるだけで、被害のリスクは劇的に下がります。
まずは自分の書いているコードで innerHTML が使われていないか、検索してみてください。それだけで、あなたはもう「セキュリティ意識の高いエンジニア」の第一歩を踏み出しています。
一歩ずつ、丁寧に。これからも一緒に学んでいきましょう!
コメント