昨年の11月にブログエントリ『
「SQLインジェクション対策」でGoogle検索して上位15記事を検証した』という記事を書いたところ、非常に好評で、「次はXSSについて書いてください」という要望をいただいておりました。中々XSSについては手がついておりませんでしたが、ようやく書いてみました。以下のURLで検索した結果の上位15位の記事を検証しました。
検索結果は変動するため、私が検索した際の結果をEvernoteの公開ノートとして記録しています。
記事の「正しさ」の検証基準としては、IPAの「
安全なウェブサイトの作り方改訂第5版」を参考に、最低限として以下が記述されているかどうかを確認しました。
- HTMLのエスケープ処理を行う
- 属性値はダブルクォートで囲む
- レスポンスヘッダのContent-Typeフィールドに文字コード(charset)の指定を行う
それでは始めます。
Webアプリケーションセキュリティの大御所、国分裕さんの記事です。2002年11月なので約10年前の記事ですね。当時としては素晴らしい内容ですが、サニタイジングという用語の使い方など、さすがに現在初心者が読むにはふさわしくないでしょう。また、レスポンスヘッダに文字コード(文字エンコーディング)を指定するという説明はありません。
さらに、この記事では、現在ではHTTPヘッダインジェクション、あるいはHTTPレスポンス分割などと呼ばれる攻撃手法もXSSの一種として説明しています。当時は、HTTPヘッダインジェクションの解説はほとんどなかったので貴重な内容だったと思いますが、現在はXSSとHTTPヘッダインジェクションは別の脆弱性として分類することが一般的だと思います。
Wikipediaの解説です。
概ね正しい内容ですが、辞書的な書き方で、初心者が参考にするには読みにくい内容です。ここでも、文字エンコーディングについての言及はありません。
徳丸の記事です。
この記事は、SQLインジェクションの改ざん対策の補助的な対策として書かれた内容ですので、XSS対策単体として読むと不十分な内容です。
セキュアプログラミング講座(旧版)の記事です。旧版の方が検索結果の上位にくるのですね。
内容は概ね正確ですが、「サニタイジング」という用語の使い方など、古さを感じます。さらに、文字エンコーディングの指定についても言及がありません。
古い記事を敢えて読む理由はないでしょう。
OKWaveでのQ&Aの記事です。2002年の内容で、回答者は国分さんの記事(#1)を参照しています。この例に限らず、質問サイトのやりとは検索結果の上位にくることが多いようですが、必ずしも正確な回答でないので、参照する人は注意が必要です。
この記事も、現時点で参考にする必要はないでしょう。
大垣さんの記事です。「落とし穴」とあるように、基本的な対策を説明したものではありません。
また、細部を確認してみると、ミスが目立ちます。
(1)ereg_replaceの使い方の間違い
ereg_replaceの引数の順番が違います。
誤: $safe_text = ereg_replace($_GET['text'], '[<>]', '');
正: $safe_text = ereg_replace('[<>]', '', $_GET['text']);
(2)悪い例の選択が良くない
「しかし,javascript文字列がなくてもブラウザがJavaScriptと認識してしまうケースは多数あります。」と指摘した上で、「javascript以外でJavaScriptと認識する文字列」を説明していますが、元の例がonmouseoverイベントでの説明ですので、javascript:はそもそも不要です(後の方で説明されています)。javascript:スキームの例を示すのであれば、onmouseoverイベントではなく、a要素のhref属性に突っ込む例を示すべきでしょう。
(3)typo
また、以下のonmouseo
rverはonmouseoverのtypoでしょう。
<b onmouseorver="alert('XSS')">XSS</b>
また、読者は、「どうやってb要素にonmouseover属性を突っ込む攻撃ができるんだろう?」と悩んでしまいそうです。通常あり得ないシナリオで説明すると、余計なところで読者に負担を掛けます。
ということで、元々初心者向きでないことと、記事のクォリティが低いという問題があります。
小邨孝明さんの記事です。小邨さんは
徳丸本のレビュアーのお一人で、同書執筆時にずいぶんとお世話になりました。
この記事は、PHPアプリケーションのセキュリティに関わるものにとっては必読です。特に、PHP固有の問題については、他の記事からは得られない貴重な内容が書かれています。XSSについても必要な内容がコンパクトにまとめられています。
また、
小邨さんのブログもPHPのセキュリティの重要な情報源です。関係者は過去記事を一通り読んでおきましょう。
入力データをあらかじめHTMLエスケープしてしまう関数が紹介されていますが、このような方法は現在では否定されています。
その理由は、プログラムの内部でHTMLエスケープ済みのデータを扱うことになり、生データが必要な場合に、一々アンエスケープしなければならず、却って煩雑になるからです。結果として、多重エスケープの問題が生じたり、エスケープもれ(すなわちXSS)が生じる原因になります。
しかしながら、PHPではこのような好ましくない書き方がかなり普及してしまっているような気がします。私がそう思う根拠は、PHPには、htmlspecialchars関数の第4引数(double_encode: 二重エスケープをしない指定)やhtmlspecialchars_ decode関数(アンエスケープ用関数)が用意されていることです。表示の際にHTMLエスケープするという標準的な書き方をする限り、これらの機能は必要ありませんし、PHP以外の言語では見かけない機能です。
あらかじめHTMLエスケープしておくという書き方は、PHPのマジッククオートと似た考え方と言えます。ご存じの通り、マジッククォートとは主にSQLインジェクション対策として、あらかじめ入力値のシングルクォートやバックスラッシュをエスケープしておくものですが、SQLインジェクション対策としては不完全である上に、多重エスケープなど副作用が大きく、PHP5.4では廃止されました。
ということで、無精をせずに、必要な都度HTMLエスケープするようにしましょう。あるいは、表示の際に自動的にHTMLエスケープしてくれるフレームワークを使いましょう。
いかにもなタイトルですが、「5つのこと」として以下が推奨されいます。
- <>"&は文字参照にする
- Javascriptに動的に文字列を渡す時はURLエンコードする
- hrefやsrcの値がURLか確認する
- php.iniでsession.cookie_httponly=on
- httpd.confでTraceEnable Off
「属性値をダブルクォートで囲む」ことと「HTTPレスポンスヘッダに文字エンコーディングを指定する」が抜けています。とくに前者は致命的です。
また、上記2の「URLエンコード」という見出しは間違いのように見えますが、本文で説明されている内容はURLエンコード(%xxという形式)ではなく、Unicodeエスケープ(\uXXXXという形式)です。すなわち本文の方は正しいのですが、見出しでの用語の間違いはまずいです。
ということで、不正確な内容です。
用語辞典の解説です。
対策としては、訪問者からの入力内容をそのまま表示せずに、スクリプトなどのコードを識別して無効化する処理を施すことが必要である。
これはダメですね。「入力内容」、「スクリプトなどのコードを識別」は余計であり、誤解を招きます。
IEのXSSフィルタの説明であり、アプリケーション側の対策の話ではありません。
対策としては以下の3つが挙げられています。
- 入力データのチェック
- クッキーに重要な情報を保存しない
- デフォルトでエスケープするテンプレートを使う
肝心のHTMLエスケープの説明がないので、XSS対策の説明としては不十分です。
LACの川口洋さんによるビデオ解説です。1分程の短いプレゼンです。特別目新しい内容ではありませんが、面白く見ることができます。
あくまで、間違った例に対する説明であり、対する正しい方法が説明されているわけではありません。
WAFの宣伝です。以下のように、まずXSS対策のプログラミングの難しさが説明されます。
クロスサイトスクリプティングは【略】一般的には「プログラマが、入力フォームを作る時にJavaScriptなどのコードを別な文字に置換するなどの対応を行うべき」と考えられています。
しかしながら、掲示板のような簡単なプログラムならいざ知らず、昨今のWebシステムは数百、数千のプログラムから構成されることが普通ですから、こうしたシステムで 全てをプログラマの努力に頼ることは難しいだろうという認識も生まれています。
これはまぁいい。「難しい」のは事実ですから。しかし、以下はどうでしょうか。
リリース後のアプリケーションにたった1箇所の問題があっただけで、それまでの検査コストがすべて無駄になる
「すべて無駄になる」はあんまりでしょう。
そこで、視点を変えてみましょう。もし、アプリケーションに対する攻撃をネットワークで防ぐことができたらどうでしょうか?
- アプリケーションの追加リリースの度に検査する必要がなくなりますので、将来の追加コストを抑えることができます
- 脆弱性検査などでも発見できない、未知の攻撃も防御することができます
- セキュリティ試験などの工数を削減でき、システム開発にかかる費用の削減が期待できます
そんな理想的な話があるのでしょうか?
実は、Citrix NetScalerを使えばWebアプリケーションに対する攻撃をネットワークレベルで防ぐことができるのです!
これは酷い。何が酷いかというと、プログラマのプログラミングレベルの対処や脆弱性検査が「すべて無駄」で、WAFを導入すればWebアプリケーションに対する攻撃を防げるとしていること、「WAFを導入しさえすれば全てOK」と明示的には書いていないものの、読者がそういう印象を持つように誘導していることです。
実際には、WAFが全ての攻撃を防げるわけではなく、あくまで保険的対策です。上記の記事では、主・客が逆になるような、間違った印象を読者に与えてしまいます。
CWE(Common Weakness Enumeration)の翻訳記事からXSSの解説です。CWEについては、
IPAの解説記事から引用します。
共通脆弱性タイプ一覧CWE(Common Weakness Enumeration)は、ソフトウェアにおけるセキュリティ上の弱点(脆弱性)の種類を識別するための共通の基準を目指しています。
1999年頃から米国政府の支援を受けた非営利団体のMITREが中心となり仕様策定が行われ、2006年3月に最初の原案が公開されました。その後、40を超えるベンダーや研究機関が協力して仕様改善や内容拡充が行われ、2008年9月9日にCWEバージョン1.0が公開されました。
CWEでは、SQLインジェクション、クロスサイト・スクリプティング、バッファオーバーフローなど、多種多様にわたるソフトウェアの脆弱性を識別するための、脆弱性の種類(脆弱性タイプ)の一覧を体系化して提供しています。
CWEは、脆弱性のハンドリングをする専門家は知っておく必要があるでしょうが、一般のWeb開発者は知らなくていいんじゃないかと思います。
XSSの説明ですが、なんか仰々しい感じで小難しいですね。例えば、以下のような箇所。
ソフトウェアにおいて信頼できない入力を受け付ける箇所を全て把握してください。
なんか、一昔前のセキュアプログラミングの教科書風です。XSS対策をする上では、入力に着目するのではなく、表示の箇所で淡々とエスケープ処理をすればよいわけで、入力の信頼できる・出来ないを分類する必要などありません。そんなことをすると、かえって対処もれ、ひいては脆弱性の原因になります。表示箇所で全てエスケープと考えた方が漏れがありません。
とりあえずのまとめ
「クロスサイトスクリプティング対策」でGoogle検索して上位15記事を検証しました。SQLインジェクションの時以上に、検索上位には良い記事が少ない状況です。上位15位の中で、開発者が読むべきXSS対策の記事は小邨さんの解説くらいでしょう。
脆弱性対処の方法をGoogleなどの検索サイトに頼るのは、賢明とは言えないというのが私の感想です。
XSS対策はどうすればよいか
それでは、XSS対策についてはどうすればよいでしょうか。
あらためて思いましたが、IPAの「
安全なウェブサイトの作りかた」が脆弱性対処の規範と言えます。全てのWebアプリケーション開発者が安全なウェブサイトの作り方を学ぶべきだ思います。同書のチェックリストから、XSSの項目を引用します。
脆弱性の種類 |
対策の性質 |
実施項目 |
HTMLテキストの入力を許可しない場合の対策 |
根本的解決 |
ウェブページに出力する全ての要素に対して、エスケープ処理を施す。 |
根本的解決 |
URLを出力するときは、「http://」や 「https://」で始まるURLのみを許可する。 |
根本的解決 |
<script>...</script> 要素の内容を動的に生成しない。 |
根本的解決 |
スタイルシートを任意のサイトから取り込めるようにしない。 |
保険的対策 |
入力値の内容チェックを行う。 |
HTMLテキストの入力を許可する場合の対策 |
根本的解決 |
入力されたHTMLテキストから構文解析木を作成し、スクリプトを含まない必要な要素のみを抽出する。 |
保険的対策 |
入力されたHTMLテキストから、スクリプトに該当する文字列を排除する。 |
全てのウェブアプリケーションに共通の対策 |
根本的解決 |
HTTPレスポンスヘッダのContent-Typeフィールドに文字コード(charset)の指定を行う。 |
保険的対策 |
Cookie情報の漏えい対策として、発行するCookieにHttpOnly属性を加え、TRACEメソッドを無効化する。 |
上記に書いてない内容としては、IEのContent-Type無視問題くらいです。これも本来ブラウザ側の問題だとは思いますが、IEのシェアを考えると無視できない問題でしょう。以下を参照して下さい。
拙著「
体系的に学ぶ 安全なWebアプリケーションの作り方」を読んでいただけば、上記のことは全て具体的に解説しています(PR)。
また、AjaxアプリケーションのXSSについては、基本はHTMLのエスケープとContent-Type無視問題への対処ですが、具体的には以下の記事をお読み下さい。
このAjaxアプリケーションのXSSについては、
PHPカンファレンス北海道にて解説する予定です。
[PR]
WAF始めました。詳しくは
HASHコンサルティング株式会社まで。
「
安全なWebアプリケーションの作り方」
DRMフリーのPDFによる電子版もあります。