今回はWebアプリにおけるセキュリティ攻撃の1つであるXSS(クロスサイト・スクリプティング)について解説をしていきます。
- XSS(クロスサイト・スクリプティング)の概要
- XSSが実行されるまでの流れ
- XSSが起こる原因
- XSSの対策方法3つ(PHP)
それでは早速見ていきましょう!
セキュリティ対策についてしっかり学ばれたい方にはこちらの書籍がおすすめです。
(通称:徳丸本と呼ばれる「Webアプリ開発者必読」とまで言われている書籍です)
体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 脆弱性が生まれる原理と対策の実践
徳丸 浩/著 SBクリエイティブ/出版│Amazon
XSS(クロスサイト・スクリプティング)とは?
攻撃者が脆弱性のあるWebサイトに悪意のあるスクリプト(JavaScriptコード)を埋め込み、一般ユーザが使用しているブラウザ上においてスクリプトが実行されることによって、重要な情報を抜き出したり、改ざんなどを実行する攻撃
もう少しかみ砕いて言うと、掲示板サイトなどに投稿された悪意のあるリンクを一般ユーザがクリックすることで、
- クリックしたユーザ本人の情報を抜き取る
- 攻撃対象のサイト上のコンテンツを偽の情報に書き換える
といったことが実行される攻撃です。
実際に抜き取られる情報としては、重要情報を格納しているクッキーが挙げられます。クッキーの中にはログイン情報に関するセッションIDも含まれているため、いわゆるセッションハイジャックという別の攻撃にも繋がってしまう可能性があるのです。
XSS攻撃が行われる流れ
ここで、イメージを持ちやすくするためにXSS攻撃実行の具体的な流れを見てみましょう。
- 掲示板などの「一般ユーザ」が任意のテキストを投稿できる場所に、「攻撃者」が悪意のあるスクリプトを含めたリンクを投稿する。(Youtubeのコメント欄など)
- そのリンクを「一般ユーザ」がクリックすることで、「リンク先(脆弱性があるサイト)」へページ遷移する。
- 「リンク先」が開かれると同時に、悪意のあるスクリプトが実行される。(これにより、例えば偽物の入力フォームなどが表示される)
- ユーザがさらにボタンをクリックするなどしてしまうことで、クッキーなどの重要情報が「攻撃者」に漏洩する
補足として、「悪意のあるスクリプトを含めたリンク」とは例えば以下のようなURLのことを言います。
https://example.com?user=<script>alert(‘This is evil message’);</script>
太字になっている部分が「悪意のあるスクリプト」のことで、上記のようなリンクをクリックすることで以下のような流れが起こります。
- https://example.comへ遷移
- それと同時にhttps://example.comへ、userというキー名に<script>~</script>という文字列を格納したデータをGET送信する
- https://example.comが開かれると、<script>~</script>の部分がブラウザ上で実行される
上記のスクリプトではただ単にポップアップウィンドウを表示するだけですが、攻撃者はここに悪意のあるスクリプトを含めて情報を盗み取ったり、遷移先ページ内のコンテンツを改ざんし、混乱を招くといったことを行うわけです。
ブラウザがscriptタグで囲まれた部分をJavaScriptのプログラムだと認識して実行するという仕組みを悪用しているんですね。
XSSの対策方法3つ【PHP】
XSSの原因
XSS攻撃が成功してしまう主な原因は以下の通りです。
- 入力フォームにユーザが入力した値をサニタイジング(無害化)せずブラウザへ表示している
- 入力フォームで入力値のチェックを行っていない
- 表示するページに文字コード(charaset)を指定していない
逆に言うと、上記に対してしっかりと対策を施すことでXSS攻撃が成功してしまう可能性を大幅に減らすことが可能です。
対策① サニタイジング(無害化)する
XSS対策におけるサニタイジング=無害化とは、いわゆるエスケープ処理のことです。
具体的には、XSS攻撃で使用される&,<,>,”,’の5文字の特殊文字を「<」や「>」といった文字列に変換することで、ブラウザに単なる文字列として解釈させることができます。
XSSでは悪意のあるスクリプトを埋め込む際にscriptタグが使用されることは前述のとおりですが、つまり
<script>~</script>の部分を単なる文字列に置き換える=エスケープしてしまうことで、悪意のあるスクリプトを文字通り無害化できる
ということです。
実装例
<?php
// 文字エンコーディング(置換)にはUTF-8を使用する
echo htmlspecialchars("出力する文字列", ENT_QUOTES, 'UTF-8');
?>
上記のように記述することで、「出力する文字列」の部分を無害化(サニタイジング)した上でブラウザに表示することが可能となります。
第2引数のENT_QUOTESを指定することでシングルクォーテーション「’」とダブルクォーテーション「”」もエンコードの対象になります。
また、上記は文字コードにUTF-8が指定されているファイルに記載する際の例です。第3引数で「このファイルの文字コードはUTF-8なのでそれに基づいてエスケープ処理して下さい」と伝えています。
対策② 入力値をチェックする(バリデーションチェック)
入力時点で制限を設けておくことにより、XSSの自由度を大幅に削ることが可能です。
例えば、電話番号の入力欄に「数字のみ」を許可するようにバリデーション(検証)を実装することでスクリプトの挿入自体が不可能となります。
あるいは、文字制限を設けるだけでもスクリプトの入力を抑えることが可能となり、こちらもXSSに有効な対策となります。
実装例
以下のコードは半角数字のみ許可する場合の例です。
if( preg_match( '/^[0-9]+$/', '入力された文字列' ) ) {
// 入力された文字列が半角数字のみの場合の処理
}else{
// 入力された文字列が半角数字のみでない場合の処理
}
ここで重要なのが以下の2か所です。
- preg_match( ~ )
- ‘/^[0-9]+$/’
preg_match()関数は、第2引数で設定した文字列のパターンが、第1引数に設定した正規表現と一致するかどうかを判定する関数です。
また、‘/^[0-9]+$/’の部分で、正規表現によって「0~9の半角数字」の入力のみを指定しています。
もう少し詳細に書くと、
0~9の半角数字([0-9])で始まり(^)、その後ろには0~9の半角数字([0-9])のいずれかが連続して続き(+)、0~9の半角数字([0-9])いずれかで終わる($)文字列
という意味になります。
(頭と最後についているスラッシュ(/)は「デリミタ」と呼ばれるもので、正規表現パターンの範囲を表すためのものです。)
正規表現を使えば、例えばハイフン付きの電話番号、メールアドレスの形式といった特定のパターンの文字列だけを許可するバリデーションの実装が可能です。
実際にバリデーションを実装する際はご自身のアプリに合わせた実装をしましょう。
このようにバリデーションを実装することで、XSSで利用される悪意あるスクリプトの入力そのものを抑制できます。
対策③ 表示するページの文字コードを指定する
分かりやすく言うと、ブラウザに表示するページで使用している文字コードを明示するということです。
通常、ブラウザはHTTPヘッダに文字コードの指定があれば指定通りに、指定が無い場合には独自のルールに基づいて文字コードを予測し、画面表示処理を実行します。
独自のルールとは、例えば
HTMLテキストの冒頭を参照し、特定の文字列が含まれていたときには特定の文字コードであると解釈する
といったものです。
攻撃者はこの独自のルールを悪用し、ブラウザに対して「このファイルはこういう文字コードで記述されていますよ」と誤認させるような文字列をファイル内に仕組みます。
ここで、前述したサニタイジングのコードを改めて提示させて頂きます。
以下のコードの第3引数に注目して頂くと「UTF-8」の指定があります。
<?php
// 文字エンコーディング(置換)にはUTF-8を使用する
echo htmlspecialchars("出力する文字列", ENT_QUOTES, 'UTF-8');
?>
これにより「表示する文字列をサニタイジング(無害化)する際にはUTF-8に基づいてください」と指示を行っています。
そのため、攻撃者がブラウザ独自のルールを悪用し、ブラウザに「表示するファイルがUTF-8ではない別の文字コードで記述されている」と誤った解釈をさせた場合、サニタイジングが上手く実行されず、結果として悪意あるスクリプトが実行されてしまう恐れがあるのです。
これを防ぐためにも、表示するページ(ファイル)の文字コードを指定することが大切になります。
文字コードを指定する方法を3つご紹介します!
実装例① header関数でContent-Typeヘッダに文字コードを指定する
<?php
// ブラウザに送信データがHTML、文字コードがUTF-8ということを指定
header('Content-Type: text/html; charset=UTF-8');
?>
上記のように記述するだけで、そのPHPファイルで生成されたHTMLページの文字コードが「UTF-8」であることを指定できます。
ここで1つ注意点がありますので公式ドキュメントから引用させて頂きます。
覚えておいて頂きたいのは、header() 関数は、 通常の HTML タグまたは PHP からの出力にかかわらず、すべての実際の 出力の前にコールする必要があることです。
https://www.php.net/manual/ja/function.header.php
つまり、header()関数はHTMLタグやPHPのechoなどの画面表示に関わる記述よりも前の行に記述をしましょうということです。
文字コードの指定方法としてはこれが最も確実です。
実装例② php.iniのdefault_charsetで指定する
php.iniに以下の記述をすることでも文字コードが指定できます。
default_charset = "UTF-8"
実装例③ HTMLのhead要素内でcharasetを指定する
php.iniの変更が出来ないような状況の場合は、HTMLのhead要素内で文字コードを指定するのも有効です。
<meta charset="UTF-8">
以上がPHPでアプリ開発をするに必要なXSS対策でした。
まとめ
ここまで、XSSの概要や攻撃の流れ、PHPでの対策方法について解説をしてきました。
最後に、対策方法3つを改めて整理します。
- サニタイジング(無害化)する
- 入力値をチェックする(バリデーションチェック)
- 表示するページの文字コードを指定する
XSSが成功してしまうようなサイトを公開してしまうと、サイト運営者ではなく一般ユーザに多大な迷惑、被害を与えてしまう可能性があります。
安全なアプリ開発をするためにも、このブログだけではなく、ぜひIPAの資料や市販の書籍にも目を通してセキュリティ対策への理解を深めてみて下さい。
最後に、僕がセキュリティ対策を学ぶ際に利用した書籍をご紹介させていただきます。
体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 脆弱性が生まれる原理と対策の実践
徳丸 浩/著 SBクリエイティブ/出版│Amazon
こちらの書籍は通称徳丸本と呼ばれており、Webセキュリティの第一人者である徳丸浩さんが手がけられている書籍です。Webアプリを開発する方やWebアプリケーションのセキュリティ対策を徹底的に学びたい方にとてもオススメできる一冊です。
Webアプリケーション開発において、ある意味セキュリティ対策は技術力以上に重要です。
こちらの書籍を読むことでWebアプリケーションにおいて必要なセキュリティ要件も明確となるため、Webアプリ開発に携わる方は必ず目を通しておきたい一冊です。
最後までお読みいただきありがとうございました。
また別の記事でお会いしましょう!