SQLインジェクション対策はおすみですか?
開発開始時点からのコンサルティングから、公開済みWebサイトの脆弱性検査、
脆弱性発見後の適切な対策まで
トップ 最新 追記
過去の日記

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>
サニタイピング文字列+対抗文字列+XSS文字列
 私の環境ではすぐには mod_imagefight を試せそうにないので、代わりに竹迫さんのサニタイズ用テキストと攻撃コードを並べた文字列を埋め込んだ画像を作成してみました。右の画像がそうで、IEで画像のリンクをクリックすると、画像が表示され、alertによるダイアログが表示されます。画像が表示される理由は、サニタイピング文字列の3行目に<IMG src=#>があるためですね。
 えーっと、竹迫さんの実装では、サニタイピング文字列はコメント欄などを利用して埋め込むのだと思いますが、それは面倒だったので、全てパレットテーブルに押し込んでいます。本質的な差はないと思いますが、私の思い違いであればご指摘ください。
 この画像の16進ダンプを以下に示します。ハイライトしている部分がパレットの先頭部分で、サニタイピング文字列+対抗文字列+XSS文字列の順に並んでいます。

画像の先頭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対策の決定版になるかどうかは、もう少し検証が必要だと思いますので、引き続き検討したいと思います。
ありがとうございました。

本日のツッコミ(全1件) [ツッコミを入れる]

TAKESAKO [元の防御コードの中でstyleの閉じタグが抜けていましたね。こちらのミス。 - 4: "<style>bod..]

本日のリンク元 | 1 | TrackBacks(2)

2007-08-21 アンチ「サニタイびんぷ」

画像版サニタイズ言うな(2)

 前回のエントリーの後、mod_imagefightのコマンドライン版を作ったりして、検証してみました。まだ検証途中ですが、今回はBMP形式についてのアンチmod_imagefightについて紹介します。
 まず、BMP形式のヘッダは以下のようになります(カッコ内はバイト数)。

0000:MARK(2) ='BM'
0002:ファイルサイズ(4)
0006:予約1(2) =0
0008:予約2(2) =0
000A:ビットマップ開始位置(4)
000E:ヘッダサイズ(4) =0x28
0012:イメージ横幅(4)
0016:イメージ高さ(4)
001A:プレーン数(2) =1
001C:ピクセルあたりのビット数(2) 1,4,8,24
001E:圧縮形式(4) =0,1
0022:圧縮後のイメージサイズ
0026:水平解像度(4)
002A:垂直解像度(4)
002E:使用色数(4)
0032:重要な色数(4)
0036:カラーパレット BGR0 の繰り返し(8ビット以下の場合のみ)
XXXX:サニタイジング文字列 ※注
ZZZZ:ビットマップ
 上記で、サニタイジング文字列(※注)については、mod_imagefightが挿入する部分です。BMPは上記のように単純な形式のため、カラーパレットの後にサニタイジング文字列が来ています。本来はカラーパレットの前にこれを起きたいのですが、BMPの構造上、それは不可能と思われます。id:takesakoさんも相当苦労されたのではないかと思います。
 このため、カラーパレット(を含むヘッダ部分)については、サニタイジング文字列以外の方法で対処が必要となります。BMPの場合、カラーパレットは最大1024バイト確保されるので、とくにカラーパレットに対する対策は重要です。
 mod_imagefightで採用されている方法は次の二点です。
 まず、通常カラーパレットは24ビット(3バイト)で表現されますが、BMP形式の場合は、ワード境界に合わせたのか、カラーパレットは一色あたり4バイト使います。4バイト目は予約領域であり、0が入ることになっていますが、ここに攻撃用の文字列を挿入することは可能です。
 このため、mod_imagefightでは、この4バイト目の領域を強制的に0にリセットしています。
 次に、RGB値に対しては、たまたま'<'や'>'の値になった場合は、値を一つ増やすことによって'<'や'>'の出現を防止しています(関数sanitize_RGB)。ここは、mod_imagefightでもっとも苦しいなぁと感じるところで、微妙に色が変化することになりますが、実用上はそれほど問題にはならないのでしょう。きっと。
 さて、この前提で、mod_imagefightに対する攻撃手法を検討します。
 BMPは単純な構造であり、またヘッダの多くはmod_imagefightによる正規化により値をあるべき値に変更されています。攻撃文字列のほとんどは、カラーパレットに置くしかないでしょう。しかし、この部分には、肝心の'<'と'>'を使うことができません。
 唯一、mod_imagefightが見逃している領域として、「002E:使用色数(4)」があります。ここにタグを書くことにしましょう。といっても、'<'は一つ書くのがせいぜいなので、JavaScriptはイベントハンドラの中に書くことにします。使用色数は4バイトしか使えないので、<IMG>を使うことにして、JavaScriptはonloadイベントとして記述します。
使用色数:<IMG
パレット: SRC="a.png" onload="alert('xss');" 
ただし、このままでは、カラーパレットに対する4バイト目のゼロクリアで、JavaScriptがズタズタに切り裂かれますので、3バイトずつに区切った上で、間に一文字入れておきます。以下では、区切りを「~」で表現していますが、ここはゼロクリアされるので、値は何でもかまいません。IEがヌル文字(値0の文字)を無視する性質を利用しています。
パレット: SR~C="~a.p~ng"~ on~loa~d="~ale~rt(~'xs~s')~;" 
 この状態で、mod_imagefightを通すと、以下のようになります。

図1:mod_imagefight後

 また、IEでこの画像を表示されると、以下のようにJavaScriptが起動されます。a.pngが存在することが必要ですが、置き場所は調整可能です。

  図2:攻撃例

 この攻撃への対策は簡単だと思いますので、id:takesakoさんが速攻で修正してくださることでしょう。イメージファイトはまだまだ続く・・・

PS.
 mod_imagefightにはGET32LEとかGET32BEというマクロが出てきます。LEとBEはそれぞれリトルエンディアンとビッグエンディアンを表すような気がしますが、名前と実体が逆になっているようです。ご確認を。

参考文献
T.Teradaの日記 - [セキュリティ]ImageFight2
葉っぱ日記 - Apacheに埋め込まれたイメージファイトと戦う文字コードな方法

追記

id:takesakoさんからはてなブックマークコメントを頂戴しました。
>ゼロじゃない別の文字でパディングする方法も検討してみます。
これは巧妙な方法ですね。カラーパレット中の予約領域をゼロでクリアしているために、却ってIEがゼロ(ヌル文字)を無視するという仕様を悪用されるわけで、意味のある文字を挿入したら、パレット領域に攻撃文字列を仕込むことは非常に困難になるでしょうね。
# どの文字だったら、攻撃が成立し得るかを考えることは、挑戦的なパズルではありますが。

今後の改良に期待します。


2007-08-29 TwitterのXSS対策は変だ

Twitterのクロスサイト・スクリプティング(XSS)対策は変だ

Twitterが流行している。私もヘビーユーザとは言えないものの、結構愛用している(http://twitter.com/ockeghem)。このTwitterのXSS対策が変だなと思う事象があったので報告する。

あらかじめお断りしておくが、TwitterにXSS脆弱性がある(実際にはあったようだが)という報告ではなく、対策の方法がおかしいという報告である。
まずは、どうも変だと思うようになった事例を紹介する。

事例1 モバイル版の二重エスケイプ

私は主に、通勤などの移動中に、W-Zero3で閲覧・書き込みしている。モバイル版(http://m.twitter.com/)を主に利用しているが、「<」などの記号が二重にエスケープされていることに気がついた。

twitter.comでの表示
画像の説明



m.twitter.comでの表示
画像の説明




事例2 検索画面が変
 「Find&Invite」という機能でお友達候補を検索できるのだが、この機能が変だ。「lt」という単語で検索すると、「<」がヒットする。検索結果の例を下図に示す(私のプロファイル)。

画像の説明



しかしこの表示は実はおかしくて、私のプロファイルは実際には下図のように設定されているのだ。

画像の説明



すなわち、ltで検索すると、「<」がヒットされていることになる。そして、ネタばれのように、検索結果表示では「<」の部分が「&lt;」と表示される。


3.事例3 文字数制限が変

Twitterの氏名欄(Full Name)は20文字までという制限になっているが、「<」などの文字を使うと、より少ない文字しか入らない。ここで、「<1234567890123>」という15文字の名前を入れてみる。
すると、下図のように、「<1234567890123&gt」というように文字列が化けてしまう。
画像の説明


おそらく、内部的には「&lt;1234567890123&gt;」と格納しようとしたものの、これだと21文字になるので、末尾のセミコロンが削除されてしまったのだろう。

結論:Twitterは、HTMLエスケープされた状態で情報を保存している

もういいだろう。ソースを確認したわけではないが、これだけ状況証拠があれば十分だ。TwitterはHTMLエスケープされた文字列をDBに保存していることは間違いない。
しかし、この方法は間違っている。HTML表示する直前にHTMLエスケープするという原則に反しているからだが、その結果として、上記に見るような悪影響が出ている。設計ミスというほかないだろう。

さて、文字列をHTMLエスケープされた状態で保存したために悪影響が出ていることは報告の通りだが、この方法のメリットはあるだろうか?一つ思いつくことは、DB格納の段階でまとめてHTMLエスケープすることにより、HTMLのエスケープもれによるXSS脆弱性が発生することを防ぎたかったのかもしれない。
しかしながら、この方法ではかえってXSS脆弱性を招きやすいと私は思う。DBの値はエスケープ済みだが、画面からの入力値(などDB以外の値)はエスケープされていない。両者が混在することによって、エスケープすべきものとしなくてよいものの区別が煩雑になり、結果としてXSS脆弱性が発生しやすくなると思われる。現実問題、TwitterではXSS脆弱性が報告されているようである。

TwitterのXSS対策はサニタイズではなくエスケープという正しい方法を使っているが、エスケープする場所がよくなかった。Twitterそのものの改良も期待するが、読者が抱えるプロジェクトのセキュリティ対策の参考になれば幸いである。

追記(2007/08/29 19:30)

<と>以外の記号について調べると、「&」、「"」、「'」については何もエスケープされていないことが分かった。ひどい仕様だ。
このため、画面から「&lt;」と入力すると、これらの記号はそのままDBに入り、そのまま表示されるので、「<」に文字化けする。その他、XSSになりそうな爆弾もありそうだ。
twitter.comのようなアドホックなXSS対策は見たことがなく、ある意味興味深い。

本日のツッコミ(全2件) [ツッコミを入れる]

rna [& のエスケープですが、HTMLの文字参照になっている場合はエスケープしないという仕様のようです。&foo; とかす..]

徳丸浩 [rnaさん、ツッコミありがとうございます。&のエスケープの件、ありがとうございます。そういうことですか。しかし、一貫..]


SQLインジェクション対策はおすみですか?
開発開始時点からのコンサルティングから、公開済みWebサイトの脆弱性検査、
脆弱性発見後の適切な対策まで
トップ 最新 追記