2007-08-07 2007-08-07 mod_imagefight考
● 画像版サニタイズ言うな
しばらく前から、竹迫さんのイメージファイト(mod_imagefight)が、第10回セキュリティもみじセミナーなとで発表され話題になっていましたが、LL魂で発表されたプレゼン資料が公開されましたので、私もようやく内容を見させていただきました。
先日、PHPの攻撃コードが隠された画像ファイルが、大手ホスティングサイトで発見されたとの報道がなされました。GIF,PNG,JPEG,BMP形式の画像ファイルには、PHPのRFI攻撃で使用されるコードやJavaScriptのソースなどを埋め込むことができます。画像に埋め込まれた攻撃コードと戦う5つの方法について解説し、安全な画像アップローダの実装について考察します。
[TAKESAKO @ Yet another Cybozu Labs: LL魂お疲れ様でした[LLSpirit]より引用]
公開されただけで61枚のプレゼン資料、4分間のライトニングトークですからいつもながら大変なものです。おそらく会場はやんやの喝采だったことと推察します。
で、その中で提案されている mod_imagefight の手法ですが、画像によるXSSやRFI攻撃(Remote File Inclusion Attack)を防止するために、画像ファイル中に、PHPやJavaScriptを無効化するコード(テキスト)を挿入するものと理解しました。詳しくは、竹迫さんの素晴らしいプレゼン資料を参照ください。
攻撃を予防するためのコードは、以下のようなものだそうです(C言語ソース形式、行番号は徳丸が付与)。
1:static const char antixss[] =
2: "\"'*/-->-->]]>\n\n"
3: "<img src=# style=position:absolute;top:15;left:10;visibility:visible>"
4: "<style>body{font-size:0;visibility:hidden}"
5: "<plaintext style=display:none><?php die;?>";
つまり、<style>や<plaintext> (PHPについては<?php die;?>)を挿入することにより、後続の<script>などを無効化しようというたくらみですね。
これはサニタイジングと呼ばれる手法の一種と考えられ、竹迫さんのプレゼン資料でも、サニタイGIF(sanitigif)とかサニタイピング(sanitipng)という造語を使って説明されている。このあたりのセンスはサスガですね。その一方で、同じプレゼン資料には、サニタイズ org という言及もみられます。これの意味するところは(プレゼンを聞いていないので)分かりませんが、想像するに、以下のどちらかではないでしょうか。
- サニタイズという言葉を使うと某方面から批判が出る
- サニタイズという手法は本質的にアドホックなもので、対応に限界がある
ここでは後者について検討してみます。
サニタイズに使用しているコードを見ると、スタイル指定<style>により<script>を無効化するとともに、念のため<plaintext>を併用して、万一<style>に対する対策がなされても、<plaintext>により<script>を無効にしているように見えます。
しかし、この対策は、攻撃者側が</style>や</plaintext>を攻撃コードに含めることで対応できそうです。攻撃コードの例を以下に示します。
</plaintext></style><script>alert('xss');<script>
私の環境ではすぐには mod_imagefight を試せそうにないので、代わりに竹迫さんのサニタイズ用テキストと攻撃コードを並べた文字列を埋め込んだ画像を作成してみました。右の画像がそうで、IEで画像のリンクをクリックすると、画像が表示され、alertによるダイアログが表示されます。画像が表示される理由は、サニタイピング文字列の3行目に<IMG src=#>があるためですね。
えーっと、竹迫さんの実装では、サニタイピング文字列はコメント欄などを利用して埋め込むのだと思いますが、それは面倒だったので、全てパレットテーブルに押し込んでいます。本質的な差はないと思いますが、私の思い違いであればご指摘ください。
この画像の16進ダンプを以下に示します。ハイライトしている部分がパレットの先頭部分で、サニタイピング文字列+対抗文字列+XSS文字列の順に並んでいます。

というわけで、サニタイズという手法では、割合簡単に対抗策が取られてしまうのではないかという疑問を持ちました。
僕のオオボケであればご指摘ください。
テスト用に、サニタイピング文字列なしバージョンも用意しましたので、よろしければご利用ください。
さて、イメージファイトの改良についてですが、サニタイズ手法を使っている限り、攻撃者との「いたちごっこ」はつきまとうと思っています。画像のアップロード処理は、たとえばウィルスチェックが必要な場合もあるでしょうから元々重たい処理なのだと割り切って、もっと画像の中身をいじらないとしょうがないのではないかと思います。
しかし、どうしてもサニタイズ的アプローチでやりたいのであれば、現状の<style>や<plaintext>ではなく、以下のようにJavaScriptで別のURIに遷移してしまったらどうでしょうか?
<script>location.href="/";</script>なんか毒をもって毒を制す風情がありますね(^-^;
この方法のデメリットは、画像を直接(<IMG>を使わないで)表示できなくなることです。しかし、そういう応用は少ないのではないでしょうか?<IMG>による表示やダウンロードであれば問題ないと思います。
追記
kazuhooku さんからはてブコメントをいただきました。「plaintext の閉じタグサポートしてる実装はあるんだろうか」ということですが、確かに主要なブラウザは</plaintext>を実装していませんね。ケータイのブラウザなどは対応していますが、なにせ今はIE固有の話題をしているわけで、失礼しました。なんとなく、(本当は終タグが必要だという意識もあって)書いてしまいました。すなわち、</plaintext>は書いても書かなくても意味は同じで、本質的に<style>の中はCDATAなので<plaintext>書いても効力ないこと、<style>の方は終タグが書けるので、攻撃側対応が容易というところが問題でした。<plaintext>を前にもって来るべきでしたね。
まぁ、いずれにせよ私の結論は変わらなくて、こういうややこしいこともあるので、サニタイズ的な手法はあぶないなぁということです。
追記(2007/08/08)
作者のTAKESAKOさんから突っ込みをいただきました。<style>は、閉じていなかったとだけということで、XSS対策としては<plaintext>の方だということ。たしかに、プレゼン資料ではそうなっていたので、実装では<style>も足して「より強力に」したのかなぁと思ったのは私の勘違いでした。試したわけではありませんが、この修正版を破るのは困難だと思いますので、現実的な対策としてはmod_imagefightは有効だと思います。ただ、この手法が、画像ファイルのXSS対策の決定版になるかどうかは、もう少し検証が必要だと思いますので、引き続き検討したいと思います。
ありがとうございました。
本日のTrackBacks(全2件)
[]
元の防御コードの中でstyleの閉じタグが抜けていましたね。こちらのミス。<br><br>- 4: "<style>body{font-size:0;visibility:hidden}"<br>+ 4: "<style>body{font-size:0;visibility:hidden}</style>"<br><br>この修正で大丈夫だと思います。ご指摘ありがとうございました。