クロスサイトスクリプティングって何?

早速ですが、まずはクロスサイトスクリプティングとは何かについて説明していきます。よく「XSS」って略して呼ばれることも多いので、こちらも覚えておいてください!

あれ、Cross-Site ScriptingならCSSじゃないの?

そう思うよね。でも、スタイルシート(Cascading Style Sheets)と混同しないようにXSSと呼ばれるようになったんだよ。

なるほどね!

さて、クロスサイトスクリプティングとは、簡単に言うと、悪意のある人が、Webサイトに不正なプログラムを仕込んで、サイトを見た人に被害を与える攻撃方法のことだよ。

この攻撃ができてしまう可能性のことをXSS脆弱性があるといいます。

えっ、怖い!具体的にはどんなことが起きるの?

いろんなパターンがあるんだけど、例えば勝手に他のユーザーになりすましてコメントを投稿される、Cookieが盗まれる、偽のログイン画面が表示されてパスワードを盗まれる、といった被害が起こる可能性があるんだ。

うわぁ...それは怖いね。でも、どうやってそんなことができちゃうの?

具体的な例で説明するね!

掲示板やSNSのコメント欄に、見た目は普通のコメントだけど、実は悪意のあるプログラムが含まれているものを投稿するよ。

普通のコメントの中に<script>タグでプログラムが入ってるんだね。見た目だと、「こんにちは!」だけが表示されるのかな。

そうそう。<script>タグは、HTMLでプログラムを実行するためのタグだね。これが実行されると、そのページを見た人の情報が盗まれちゃいます。

なるほど!

これはクロスサイトスクリプティングの一例で他にも種類があるから紹介するね。

クロスサイトスクリプティングの種類

クロスサイトスクリプティングには、主に3つの種類があります!

3つも?どんな種類があるの?

まず「反射型XSS」というものがあるんだ。これは、URLのパラメータに悪意のあるプログラムを仕込む方法だよ。パラメータっていうのは、こんな感じでアドレスの最後につく「?」以降の部分のことです。

ここに例えばこんな感じでプログラムを入れることができちゃうんだ。そしてこのURLを開くとこのプログラムが実行されます。

なるほど。URLって、簡単に共有できちゃうよね...

そうそう。「このURLをクリックして!」みたいなメールで誘導されて、被害に遭うケースもあるんだよ。

リンクをクリックしただけで被害に遭うなんて怖いね。クリックする前にちゃんと確認しないと。

次は「蓄積型XSS」。これは、さっき説明した掲示板での例みたいにサーバーに悪意のあるプログラムを保存しておく方法だよ。

さっきの例だと、投稿がDBに保存されていて、サイトを見に来た他の人みんなが被害に遭っちゃうんだよね。場合によってはすごく沢山の人に被害が出る可能性があるね。

その通り!だから特に危険なんだ。

最後は「DOM Based XSS」というものがあるよ。

うーん、DOMってそもそも何だっけ。

DOMは「Document Object Model」の略で、Webページの内容を木構造で表現したものなんだ。

木構造?どういうこと?

ちょっと例を見てみよう。例えばこのHTMLは、htmlが一番上にあって、その下にbody、その下にdiv、さらにその下にpというような木の枝みたいな構造になってるんだ。

あー、なるほど!枝分かれしてるって考えるんだね。

その通り!で、DOM Based XSSは、JavaScriptがこの構造を操作する時に発生する脆弱性なんだ。

例えば、こんなJavaScriptのコードがあったとします。

うーん、どういう処理をしてるの?

このコードは、URLの最後に#まりん って付けると、「こんにちは、まりんさん!」って表示してくれる処理なんだ。1行目でURLのパラメータからnameの値を取得して、2行目で”greeting”というidを持つHTMLの要素をこんにちは name さん!という文に置き換えているよ。

URLの値によって、画面に表示する要素を操作してるんだね。

でもここでもし誰かが、URLの最後に悪意のあるスクリプトを入れたらどうなると思う?

あ!さっきみたいに、プログラムが実行されちゃうの?

その通り!nameの部分が悪意のあるプログラムになるってことだから画面に表示される要素にプログラムが含まれてしまいます。そのため、プログラムが実行されてアラートが表示されちゃうことになるよ。

なるほど!でも、これって最初の「反射型XSS」と同じじゃないの?URLにスクリプトを入れるってところが同じなような...

いい質問だね!

大きな違いは「プログラムがどこで実行されるか」なんだ。

反射型XSSは、ユーザーが悪意のあるURLをクリックするとリクエストがサーバーに送られ、サーバーがそのまま悪意のあるコードを含んだレスポンスを返すことでブラウザでコードが実行されるという仕組みになっています。

対してDOM Based XSSは、ユーザーが悪意のあるURLをクリックするとJavaScriptがURL内の値を読み取りその値を使ってDOMを書き換えることでブラウザでコードが実行されるという仕組みになります。

あ!反射型はサーバーを経由するけど、DOM Basedはブラウザの中、JavaScriptの処理の中だけで完結するんだね。

そうそう!サーバーのコードに脆弱性があるのが反射型で、JavaScriptのコードに脆弱性があるのがDOM Basedってことだよ。

そのため、サーバー側の対策だけじゃなくて、ブラウザ側でも対策が必要になるよ。特に最近のWebアプリケーションは、ブラウザ側でもたくさんの処理をするから、DOM Based XSSの対策は重要になってきてるんだよ。

なるほど!XSSの種類によって、対策も違ってくるんだね。

XSSの対策

では最後に、XSSはどうやって対策したらいいのかについて説明します!

XSSの対策は、大きく分けて5つのアプローチがあるんだ。それぞれ詳しく見ていきましょう!

まず「エスケープ処理」は、特殊文字を安全な形式に変換することだよ。例えばこんな感じで変換します。

これを変換することで、プログラムとしてじゃなくてただの文字列として特殊文字を扱うことができるってことだね!

その通り!<script>とかがあっても、&lt;script&gt;と変換されるのでプログラムとして認識されなくなります。これが一番重要で、必須で行うべき対策だよ。

この次から紹介するのは、さらに保険的に行ったほうがいい対策になります。

まずは入力値の検証です。ユーザーの入力値をチェックすることで予期しない入力を防ぐよ。

入力値の検証には、主に2つのアプローチがあり許可する文字や形式を明確に定義するホワイトリスト方式と、禁止する文字や形式を定義するブラックリスト方式があります。

基本的にはホワイトリスト方式の方が安全だけど、自由入力の入力欄とかではあまり対策できないので注意です。

次にHTTPヘッダーの設定です。HTTPヘッダーでは、主にこの2つの設定が重要だよ。

うーん、見たことない言葉ばっかりでちょっと難しいかも...

そうだよね。まず、X-XSS-ProtectionはXSSフィルタの設定になります。XSSフィルタとは、ブラウザが反射型XSSを検知し、無害な出力に変更する機能です。

これはデフォルトでは有効なんだけど、ユーザーが無効に設定していることもあります。そこで、X-XSS-Protectionを使うとこの設定を上書きして有効にすることができるよ。ただ、現在最新のブラウザではこれはほとんどサポートされていないので、古いブラウザ向けの対策になります。

次にContent-Security-Policy(CSP)は、Webサイトでどんなコンテンツを実行できるかを制限する機能だよ。

例えばこんな感じで書いてあったら、self、自分のサイトと同じオリジンからのスクリプトだけを実行するって設定なんだ。

なるほど!それ以外からスクリプトが来ても実行しないから安全なんだね。

そうそう!

他にも直接URLを指定して特定の送信元からのスクリプトを許可することもできるよ。最新のブラウザではX-XSS-Protectionの代わりにこちらの設定を行うことが推奨されています。

次にCookieの設定です。これは、クロスサイトスクリプティングへの対策というよりは、Cookie情報の漏えいを防ぐ仕組みだよ。CookieにHttpOnly属性というものを付けるとHTML内のスクリプトからはアクセスできないようになります。

つまり、さっきの攻撃の例みたいにJavaScriptからのアクセスはできないってことだね!

そうそう!そのため、仮に攻撃を受けたとしても、JavaScriptによるCookie情報の盗み出しを防止することができるよ。

次にDOM Based XSSの対策です。

DOM Based XSSには、1. DOMの安全な操作、2. URLパラメータの処理、3. eval()やdocument.writeの使用を避けるといった対策が必要だよ。

サーバー側の対策だけじゃなくて、ブラウザ側でも気をつけないといけないんだったね。

そうそう。具体的なソースコードを見せながらどんな対策なのか紹介していくね。

まずはDOMの安全な操作についてです。innerHTMLはHTML文字列を直接埋め込むため、悪意のあるスクリプトも実行される可能性があります。

textContentは、入力をそのままテキストとして扱い、HTMLとして解釈しないのでユーザーが<script>タグやその他の危険なHTMLを入力しても、文字列として安全に表示されるよ。そのためinnerHTMLはなるべく使わずに代わりにtextContentを使うことが推奨されます。

次にURLパラメータの処理についてです。location.hashには任意の文字列が含まれる可能性があるので、<script>タグなどの悪意のある入力をエスケープせずそのまま使用すると、プログラムが実行されてしまいます。

encodeURIComponentを使用すると、特殊文字(例: <, >, &)がURLエンコーディングされるのでHTMLとして解釈されなくなります。よって、スクリプトやその他の不正なデータを無害化することができるよ。

最後にeval()やdocument.writeの使用を避けるです。eval()は、文字列をJavaScriptコードとして評価して実行する関数です。外部から渡された不正な文字列をそのまま実行してしまう可能性が高く、セキュリティリスクが極めて高くなってしまうよ。

また、document.writeは、HTMLドキュメントに直接コンテンツを挿入するための関数です。信頼できないデータをdocument.writeで挿入すると、任意のHTMLやJavaScriptコードを埋め込めるため、XSSのリスクが非常に高くなります。

JavaScriptの関数の中でも、XSS脆弱性の原因になってしまう書き方が色々あるんだね。

そうなんだ。ユーザーからの入力値を扱うような場所では特に気を付けないといけないね!

大切なのは、これらの対策を組み合わせて、多層的な防御を実現することなんだ。一つの対策だけに頼るのは危険だよ。

そして一番大切なのは、開発者一人一人がセキュリティを意識することです。「ユーザーの入力は信用しない」という考え方が基本になるんだ。

うんうん。Webサイトのセキュリティって考えることがいっぱいあって大変そうだけど安全なサイトを作るためには大事なことなんだね!