【入門編】 クロスサイトスクリプティング(XSS)の攻撃ベクトルとDOMベースXSSの検知 – オフェンシブセキュリティ & リバースエンジニアリング防御ガイド

こんにちは!Webアプリケーションの開発や、会社のホームページの管理などを任されるようになると、ふと耳にするのが「XSS(クロスサイトスクリプティング)」という言葉ですよね。「なんだか難しそうだな…」「セキュリティの専門用語はチンプンカンプンだよ!」と感じている方も多いのではないでしょうか?

大丈夫です!一歩ずつ、身近な防犯の仕組みに例えながら優しく紐解いていきましょう。今回は、このXSSが一体どんな攻撃なのか、そしてどうやって防げばいいのかを一緒に学んでいきますね。

—

1. 家の鍵に例えて理解する「XSS(クロスサイトスクリプティング)」

突然ですが、あなたが暮らす「お家」を想像してみてください。
頑丈な玄関のドアには立派な鍵がついていて、泥棒が入らないようにしっかりと施錠していますよね。

では、もし「郵便受け(ポスト)」のフタがパカパカと開いたままで、しかも玄関の内側につながっていたとしたらどうでしょう?
泥棒はわざわざ頑丈なドアを壊さなくても、その開いたポストから「悪い仕掛け」をピョコンと投げ入れるだけで、家の中に入り込んで大切なものを盗み見たり、勝手に家の中の家電を操作したりできてしまいますよね。

Webの世界におけるXSSも、これとまったく同じなんです。

Webサイトは、ユーザーからの入力(名前やコメントなど)を受け付ける「ポスト」を持っています。もし、このポストの管理が甘くて、変なもの(悪意あるプログラム)が投げ込まれたときに、それをそのまま「お家の中(Webブラウザ)」で実行してしまう不具合があると……。

あなたのサイトを見に来てくれた優しいお客さんのブラウザ上で、勝手に怪しいプログラムが動き出し、こっそり保存されている大事な情報(セッションIDやクッキーなど)を盗み出されてしまうのです。これがXSSの恐ろしい仕組みです。

—

2. 攻撃の3つのタイプ:反射型・蓄積型・DOMベース

XSSにはいくつかのタイプがありますが、どれも「他人のブラウザで自分の好きなJavaScriptを勝手に実行させる」という目的は同じです。代表的な3つの形を覗いてみましょう。

① 反射型XSS(Reflected XSS)

その名の通り、鏡のように「跳ね返ってくる」タイプの攻撃です。
よくある検索画面などを想像してください。検索窓に文字を入力して「検索」を押すと、画面に「「〇〇」の検索結果」と表示されますよね。

ここで攻撃者は、わざと次のような怪しいURLを作ります。

https://example.com/search?q=<script>alert('乗っ取り!')</script>

このURLをうっかり踏んでしまったユーザーは、Webサイト側が「おっ、そのまま画面に表示してあげよう」と親切心からそのまま出力してしまい、被害者のブラウザ上で alert('乗っ取り!') というプログラムが実行されてしまいます。鏡のように、リクエストされた内容がそのまま「反射」して返ってくるから反射型と呼ばれます。

② 蓄積型XSS(Stored XSS)

これが一番厄介でタチの悪いタイプです。
掲示板やSNSのコメント欄などを思い浮かべてください。そこに攻撃者が悪意あるプログラムを含んだコメントを投稿すると、そのコメントはデータベースに「蓄積」されます。

その後、何気なくその掲示板を見にきた一般のユーザー全員が、その蓄積されたプログラムを読み込んでしまい、被害に遭ってしまいます。まるで公園のベンチにこっそり毒を塗っておくようなもので、自分がアクセスしていなくても、後から来た人が次々と被害を受けてしまうのが特徴です。

③ DOMベースXSS(DOM-based XSS)

ちょっと名前が難しそうですが、仕組みはシンプルです。
これまでの反射型や蓄積型は「一度サーバーにデータが送られて、それが返ってくる」という動きでしたが、DOMベースはサーバーを通らず、ブラウザの中だけで完結する攻撃です。

例えば、WebサイトのJavaScriptが、URLのハッシュ部分(#以降の値など)をそのまま読み取って、画面にペタッと書き出すようなプログラムになっていたとします。

// URLの # 以降の文字を取得して、そのまま画面の要素に書き込んでいる危険な例
let userName = location.hash.substring(1);
document.getElementById('welcome').innerHTML = "ようこそ、" + userName + "さん!";

もし、ユーザーが以下のようなURLにアクセスさせられたらどうなるでしょうか?

https://example.com/profile.html#<img src=x onerror=alert('DOM-XSSだよ!')>

サーバー側は何も悪くないのに、ブラウザのJavaScriptが勝手にそのタグを解釈して実行してしまうため、見事に攻撃が成立してしまいます。これがDOMベースXSSの正体です。

—

3. なぜ起きるのか? 生のコードで見る脆弱性の原因

なぜこのようなことが起きるかというと、一言で言えば「受け取った文字を、そのまま信用して画面に描き出してしまうから」です。

例えば、PHPを使った次のようなコードを考えてみましょう。

<!-- 危険なコードの例:入力された内容をそのまま画面に出している -->
<div>
    検索キーワード: <?php echo $_GET['q']; ?>
</div>

ここに、もしユーザーが普通の文字ではなく、HTMLのタグやJavaScriptのコードを入力してきたら、ブラウザはそれを「プログラムだ!」と勘違いして実行してしまいます。これがすべての元凶です。

—

4. どうやって防ぐの? 泥臭いけど確実な対策とHTTPヘッダー

さて、ここからが一番大切な「守り」の話です。
家の鍵を頑丈にするように、Webサイトでもしっかりと対策を施していきましょう。

対策①:出力時の「エスケープ処理」

一番基本にして最強の対策は、画面に文字を表示するときに「HTMLの特別な意味を持つ文字を、ただの文字に書き換える(エスケープする)」ことです。

例えば、< という記号は、そのまま表示すると「タグが始まった!」とブラウザが勘違いするので、&lt; という安全なコードに変換します。

PHPであれば htmlspecialchars() 関数を使うのが鉄則です。

<!-- 安全なコードの例:エスケープ処理を必ず行う -->
<div>
    検索キーワード: <?php echo htmlspecialchars($_GET['q'], ENT_QUOTES, 'UTF-8'); ?>
</div>

これをしておけば、たとえユーザーが <script> と入力しても、画面にはただの文字列として表示されるだけで、プログラムとしては絶対に動きません!

対策②:HTTPヘッダーによる二重の防御(CSP)

プログラム側でのエスケープが基本ですが、人間誰しもうっかりミスはありますよね。そこで頼りになるのが、ブラウザに送る「おままもり(HTTPヘッダー)」です。

その代表が CSP(Content Security Policy:コンテンツセキュリティポリシー) です。

サーバーからブラウザへ「このサイトでは、外部から読み込んだ怪しいスクリプトは絶対に実行しちゃダメだからね!」という強いルールをあらかじめ伝えておく仕組みです。

例えば、Webサーバー(ApacheやNginxなど)の設定、あるいはPHPのヘッダー出力で次のように設定します。

<?php
// HTTPヘッダーでCSPを設定し、インラインスクリプトの実行を原則禁止にする
header("Content-Security-Policy: default-src 'self'; script-src 'self'");
?>

これ設定しておくと、万が一コードの書き漏らしがあって怪しいJavaScriptが紛れ込んでも、ブラウザ側が「いや、ルール違反だから実行しません!」とストップをかけてくれます。まさに二重の防犯ロックですね。

—

5. まとめ:一歩ずつ、安全な開発を心がけよう

いかがでしたでしょうか?
クロスサイトスクリプティング(XSS)も、仕組みの本質を知ってしまえば「要するに、入力されたデータをそのまま信用して実行させなければいいんだな」とスッキリご理解いただけたのではないでしょうか。

実務の開発現場では、
1. ユーザーからの入力値は絶対に信用しない
2. 画面に出力するときは必ずエスケープ(無害化)する
3. CSPなどのセキュリティヘッダーを活用してブラウザにも守ってもらう

この基本をしっかりと守るだけで、ほとんどのXSSは綺麗に防ぐことができます。

最初は覚えることが多くて大変に感じるかもしれませんが、一つひとつの仕組みを丁寧に紐解いていけば大丈夫です。一緒に安全で安心なWebの世界を作っていきましょう!

コメント

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