ソフトウェアなどを使いこなすために,ストレスを感じながらもしぶしぶ覚えなければならないようなノウハウ,「バッドノウハウ」がテーマの本連載,第5回の今回はブラウザのBKを,コンテンツの扱いに関連するものに絞って取り上げたいと思います。
IEのContent sniffing
通常,ブラウザはHTTPのレスポンスのContent-Typeヘッダに応じて,コンテンツをどのように処理するか決めますが,Internet Explorer 7(IE7)はこのヘッダを無視するときがあります(注1)。
たとえば,リスト1のようなファイルをtest.txtという名前でWebサーバに置いて,IE7からアクセスすると,サーバからContent-Type:text/plain(ただのテキストファイル)として送られてきているにもかかわらず,HTMLとして解釈されてしまいます(図1)。
リスト1 test.txt
#! /usr/bin/perl
print "<html><h1>hello, world</h1></html>"
同様の現象はtext/plainに限らず,image/jpegなどの画像のContent-Typeが指定されているときにも起きます。このような挙動はContent sniffingと呼ばれています(注2)。
おそらく,この挙動はContent-Typeが間違っていてもコンテンツを正しく表示したい,という意図のもとに実装されているのだと思われます。しかし,Webアプリケーションの脆弱性につながる恐れがある挙動としても知られています。
先のリスト1の例では無害なHTMLタグが使われているだけですが,<script>タグでJavaScriptを埋め込むこともできます。これはユーザが任意のファイルをアップロードできるサイトで,とくに問題になります。テキストファイルや画像と見せかけて悪意のあるJavaScriptをアップロードして他人に開かせる,という攻撃が可能であるためです。

この自動判別の挙動は,IE8ではX-Content-Type-Options:nosniffというヘッダをHTTPのレスポンスに入れることで無効にできます。このような変な挙動は単に撤去すればいい気がしますが,この挙動に依存しているサイトを壊さないために互換性を維持しているのだと思います。

- 注1)
- IE7.0.5730.13で動作検証しました。IE6でもおそらく同様ですが,手元に動作検証できる環境がないため,今回はIE7に絞って話を進めます。
- 注2)
- Content sningに関する情報は以下が充実しています。
URL:http://webblaze.cs.berkeley.edu/2009/contentsniffing/
Content-Dispositionヘッダで解決?
前述した,本来のContent-Typeと異なる形式でコンテンツが解釈されてしまうという問題を回避するには,ブラウザにファイルを開かせる代わりに保存させればいいのでは,というアイディアがあります。具体的にはHTTPのレスポンスに,リスト2を加えます。このContent-Dispositionヘッダにより,ブラウザの中でコンテンツを開くのではなく,foo.txtというファイルとして保存せよ,とブラウザに伝えることができます。
リスト2 Content-Dispositionヘッダ
Content-Disposition: attachment;
filename="foo.txt"
しかし,IEにはContent-Dispositionヘッダの扱いに過去に問題がいろいろあり(注3),最近のバージョンでも問題が発見されているので(注4),Content sningを防ぐためにContent-Dispositionに頼るのは安全ではありません。

危険性のあるテキストファイル(注5)の内容をどうしても表示させたい場合は,Content sningの影響を受けないように先頭の256バイトを空白で埋める(注6),HTMLの特別な文字をすべて<,>などにエスケープした上でHTMLとして表示させる,といった方法があります。

ところで,Content-Dispositionというヘッダは元々,メールで使われているMIMEというフォーマットを拡張するために導入されたものであり,正式にはHTTPの仕様には含まれません。HTTP 1.1の標準であるRFC 2616を見ると,Content-DispositionはHTTPの標準の一部ではないと明記されています(注7)。しかし,大半のブラウザはContent-Dispositionヘッダに対応しているので,RFC 2616の中でもこのヘッダの扱い方について言及されています。

- 注3)
- たとえば以下によると,一部のIEのバージョンではテキストやHTMLの場合,Content-Disposition:attachmentが指定されていても,ブラウザ内で表示してしまったようです。
URL:http://support.microsoft.com/kb/267991 - 注4)
- JavaScriptのhistory.back()と組み合わせる攻撃法が知られています。
- 注5)
- ユーザがアップロードしたファイルなど。
- 注6)
- 以下によると,IEはコンテンツの先頭の256バイトでコンテンツの種類を判別します。
URL:http://msdn.microsoft.com/en-us/library/ms775147(VS.85).aspx - 注7)
- RFC 2616の15.5節「Content-Disposition Issues」より。
日本語ファイル名の問題
前節のリスト2で,Content-Dispositionヘッダのlenameパラメータで,ファイル名を指定できることを示しました。では,日本語のファイル名はどのように扱えばいいのでしょうか。これは,実は奥が深い問題です。
単純に考えると,リスト3のように日本語の文字をそのまま使えばいいような気がしますが,この場合,どの文字エンコーディングを使うかが問題になります。
たとえば,UTF-8を使うと,IE7では「縺ゅ>縺・txt」のようにファイル名が文字化けしてしまいます。
リスト3 日本語の文字そのまま
Content-Disposition: attachment;
filename=" あいう.txt"

日本語ファイル名とメールの世界
メールの世界でも,添付ファイルの日本語ファイル名を扱うのはややこしい問題として知られています。多くのメーラはリスト4のようにエンコードします。
先頭の=?UTF-8?B?は文字エンコーディングがUTF-8であること,BASE64でデータがエンコードされていることを意味しています。このようなエンコード方式はRFC 2047で定義されており,encodedwordと呼ばれています。
リスト4 よくあるエンコード
Content-Disposition: attachment;
filename="=?UTF-8?B?44GC44GE44GGLnR4dA==?="
実は,上のContent-Dispositionは厳密にはRFC 2047に違反しています。RFC 2047によると,encoded-wordは"(ダブルクォート)で囲まれた文字列の中に含めるのは禁止されているためです(注8)。
メールの標準的に正しい方法はRFC 2231に則ったリスト5のような形式です。
リスト5 RFC 2231に則ったエンコード
Content-Disposition: attachment;
filename*=UTF-8''%E3%81%82%E3%81%84%E3%81%86.txt
しかし,この「正しい」方法をサポートしていないメーラもあるため,歴史的に,前述の「正しくない」方法のほうが普及しています。
- 注8)
- 「5. Use of encoded-words in message headers」の「An'encoded-word' MUST NOT appear within a 'quotedstring'」より。


