プライバシーポリシー

2007年12月6日木曜日

画像ファイルによるクロスサイト・スクリプティング(XSS)傾向と対策

補足

この記事は旧徳丸浩の日記からの転載です(元URLアーカイブはてなブックマーク1はてなブックマーク2)。
備忘のため転載いたしますが、この記事は2007年12月6日に公開されたもので、当時の徳丸の考えを示すものを、基本的に内容を変更せずにそのまま転載するものです。
補足終わり


最近、画像ファイルを用いたクロスサイト・スクリプティングが注目されている。本稿では、画像を悪用したXSSについて説明した後、対策方法について解説する。

画像によるXSSとはどのようなものか

Internet Explorer(IE)の特性として、コンテンツの種類を判別する際に、レスポンスヘッダ内のContent-Typeだけでなく、コンテンツの内容も判断基準にしている。このため、Content-Typeが例えばimage/gif(GIF画像)となっていても、中身がHTMLであればHTMLと解釈して表示する。このため、JavaScriptが埋め込まれていたら、JavaScriptを実行することになる。 例を示そう。このファイル(html.gif)は、拡張子は.gifなので、ダウンロードするとContent-Typeはimage/gifとなるが、内容はHTMLでありJavaScriptが埋め込んである。これをIEとFirefoxで表示した例を以下に示そう。
IE7での表示
Firefoxでの表示
IEが、状況によってはContent-Typeを無視して、画像をHTMLと解釈し内部に埋め込まれたJavaScriptを実行してしまう。これによるXSSが成立する場合がある。典型的な状況とてしは、画像のアップロード機能を有するアルバム・サービスのような場合だ。たとえば、はてなの画像アップロードシステムには、この脆弱性があったとして、対応がなされたことがある。 下図は、画像によるXSSの模式図である。攻撃者(左側のサングラスの人物)は、攻撃用JavaScriptを仕込んだ画像を、maitter.com画像アップロードシステムを利用して登録・公開し、この画像のURLをダウンロードするよう、メールやブログなどで誘導する。被害者がこの誘導に引っかかって画像を閲覧すると、JavaScriptが実行され、maitter.comドメインのCookieが盗難されるなどの被害にあう。


先に示したhtml.gifは内容はhtmlテキストだけという素朴なものだが、イメージそのもの、パレット、コメントなどにhtmlやJavaScriptを仕込む手法が知られている(*1)。

対策の検討

IEがコンテンツの種類をどのように判別しているかは公開されていないようだが、たとえば2007年10月10日に公開されたマイクロソフト セキュリティ情報 MS07-057には以下のような記述がある。
PNGs が MIME スニッフィング アルゴリズム の jpg/gif と同じ状態に昇格されます。例: サーバーが png のコンテンツの種類を送信した場合、Internet Explorer は magic-byte (マジックバイト) のテストを実行し、成功した場合、権限のあるサーバーの MIME の種類として処理します。
意味のとりにくい日本語(機械翻訳?)だが、どうもコンテンツの種類判別にIEがマジックバイトを利用しているらしいことがわかる。

マジックバイトとは

マジックバイトとは、画像などのフォーマットを判別しやすいように、ファイルの先頭の数バイトに置かれた固定のデータである。GIF形式の場合であれば、"GIF87a"あるいは"GIF89a"で始まることになっており、先頭6文字を見ればGIF形式であることが容易に判別できる。下表に、主な画像形式のマジックバイトを示そう。
画像形式BMPGIFJPEGPNG
マジックバイトBMGIF8[79]a\xFF\xD8\x89PNG\x0D\x0A\x1A\x0A

IEのコンテンツ種類判別はContent-Typeとマジックバイトの組み合わせ

以上の情報などから、どうもIEのコンテンツ種類判別は、Content-Typeとマジックバイトの組み合わせにより決まるように思える。これを検証するために、主要な画像フォーマット4種(BMP、GIF、JPEG、PNG)のContent-Typeとマジックバイトの全ての組み合わせを試してみた。検証用に用いたサンプルデータは、マジックバイトとJavaScriptを組み合わせただけの簡単なものである。一例を下図に示す(GIF形式の偽画像)。
結果は下表の通りである。表で、「HTML」となっている欄はHTMLと判別され、JavaScriptが起動するもの、「画像」となっているのは、画像と解釈しようとしたが表示できないため「×」表示となることを示している。これらはリンクとなっていて、実際のサンプルデータで試すことが出来るようにした。

画像種類マジックバイトContent-Type
image/bmpimage/gifimage/jpegimage/png
BMPBMHTMLHTMLHTMLHTML
GIFGIF87aHTML画像HTMLHTML
JPEGxFFxD8HTMLHTML画像HTML
PNG\x89PNG\x0d\x0a\x1a\0aHTMLHTMLHTML画像(*2)
この表から分かることは以下の二点である Content-Typeとマジックバイトは同一の画像フォーマットに対応したものでなければならず、不一致だとHTMLとみなされる(可能性がある) BMP形式に関しては、Content-Typeとマジックバイトの両方を揃えても、なおHTMLとみなされる場合がある なお、マジックバイトのContent-TypeからPNG形式を判定するようになったのは、前述のようにMS07-057(2007年10月10日)適用後のことであるので、ちょうど2ヶ月前からのことである。

対策:ではどうすればよいか

従来、画像XSSの対策には、イメージを他の形式に変換してから元の形式に戻す、あるいは竹迫氏のmod_imagefightのように画像中にXSS防御文字列を挿入するなど、さまざまな手法が提案されている。しかしながら、対策として不十分であったり、画質や処理速度の低下を招くなどさまざまな副作用があった。 マイクロソフトがMS07-057にてPNG画像のコンテンツ種別判定を改善してくれたことから、Content-Typeとマジックバイトの判定のみで、画像XSSを予防できる可能性が高くなった。 これでもまだ、BMP形式については改善がされておらず、画像XSSの危険性が残っている。コレに対しては、BMP形式画像のアップロードを許容する場合はPNGに変換して公開するというガイドラインを提案したい。BMP形式は通常画像が圧縮されていないので圧縮して公開した方が好ましいこと、BMPの特性(色数、可逆性など)はPNGで全てカバーしていることがその根拠である。 結論としては以下のようになる。 画像のマジックバイトとContent-Typeが同一の画像フォーマットに対するものであることを確認する MS07-057を適用する(ようにユーザに促す) 例えば、BMPを避ける。あるいはPNGに変換して公開する。

謝辞:
画像埋め込みのHTML/JavaScriptについては、 はせがわようすけ氏からさまざまな情報を教授いただいた。記して、感謝の意を表する。

脚注
*1:JavaScriptを埋め込んだ画像を作ってみました参照
*2:MS07-057を適用していないとHTMLと解釈される

参考文献:
マイクロソフト セキュリティ情報 MS07-057
2007-07-15 - T.Teradaの日記 - 画像へのPHPコマンド挿入
TAKESAKO @ Yet another Cybozu Labs: LL魂お疲れ様でした[LLSpirit]
yohgaki's blog - 画像ファイルにPHPコードを埋め込む攻撃は既知の問題

本日のツッコミ(全3件)

□ はせがわ (2007年12月10日 13:54)
はてなでの2004年の対策は、MS07-034のmhtml:に対する対策だったはずです。mhtml:をURLに付与することで、あらゆるリソースがRFC557のMHTML形式のドキュメントであると強制可能だったため、画像でも何でもbase64なスクリプトが埋め込み可能でした(なのでスタイルシート内でboundaryを削除している)。IEがContent-Typeを無視するというのとは、表面的・結果的には似ていますが、原理的には異なるものでした。

□ hoshikuzu (2007年12月23日 16:26)
まさしくアレはmhtml対策でした。はてなに報告した人より。

□ えむけい (2008年08月30日 05:49)
IE8でサンプルデータを試してみたらBMPはすべてtext/plainで(JavaScriptは起動せず)、GIF/JPG/PNGはすべて壊れた画像アイコンで表示されました。
ようやく画像XSSも過去のものになりそうです。