本当は怖い文字コードの話

第2回UTF-7によるクロスサイトスクリプティング攻撃[後編]

前回に引き続き、UTF-7によるクロスサイトスクリプティング(XSS)について説明していきます。

UTF-7によるXSSは、攻撃対象のコンテンツの文字エンコーディングが不明瞭な場合に、そのコンテンツを被害者のブラウザ(Internet Explorer)で開いたときに、そのコンテンツの文字エンコーディングがUTF-7であるとIEに誤認させ、⁠+ADw-script+AD4-」のようなUTF-7の文字列が有効なHTML要素として認識されるために発生します。

そして、⁠文字エンコーディングが不明瞭」な具体的な状況として、以下のような条件のいずれかに該当するということを前回説明しました。

  • レスポンスヘッダ、meta要素のどちらでもcharsetが指定されていない
  • charsetにIEが解釈できないエンコーディング名が指定されている
  • meta要素でcharsetを指定しているときに、meta要素より前に攻撃者が文字列を挿入可能

これらの条件において、なぜUTF-7によるXSSが可能になるのか、そのメカニズムについて詳細に説明していきます。

レスポンスヘッダ、meta要素のどちらでもcharsetが指定されていない

たとえば、次のようなHTTP応答があったとします。

HTTP/1.1 200 OK
Content-Type: text/html

<html>
<head><title>test page</title></head>
<body>
+ADw-script+AD4-alert(1)+ADw-/script+AD4-
</body>
</html>

見ての通り、HTTPレスポンスヘッダ、HTMLのmeta要素内のいずれにも文字エンコーディングの指定がありません。

IEを使ってこのようなHTMLを開いた場合、IEにおいて文字エンコーディングの自動選択が有効になっていると、+ADw- のような特徴的な文字列からコンテンツがUTF-7であると自動で解釈され、スクリプトが動作します。

あるいは、IEの文字エンコーディングの自動選択が無効であったとしても、攻撃者がUTF-7で書かれた罠ページを用意し、そこからiframe経由で攻撃対象のページを読み込んだ場合には、iframe内は親ページの文字エンコーディングを引き継ぐため、攻撃対象のページもUTF-7であると解釈され、やはりスクリプトが動作します。

<meta http-equiv='content-type' content='text/html;charset=UTF-7'>
...
<iframe src='http://example.com/target.html'></iframe>

charsetにIEが解釈できないエンコーディング名が指定されている

HTTPレスポンスヘッダあるいはHTMLのmeta要素内で文字エンコーディング名を指定したとしても、それがIEに理解できない名称であれば、文字エンコーディングが指定されていない場合と同様に、IEの文字エンコーディングの自動選択や攻撃用iframeの経由でコンテンツをUTF-7と見なしてスクリプトを動作させることが可能となってしまいます。

IEが理解できない文字エンコーディング名としてよくあるのは、以下のようなものです。

  • EUC ⁠正しくは "EUC-JP" です)
  • UTF8 ⁠正しくは "UTF-8" です)
  • JIS ⁠"ISO-2022-JP"を指定します)
  • CP932/MS932 など ⁠"Shift_JIS"を指定します)

これらが文字エンコーディング名として "text/html;charset=UTF8" のように指定されていた場合、IEは文字エンコーディングが不明であるため、上記のとおりUTF-7と解釈してしまう可能性が高くなります。

IEが確実に解釈できるエンコーディング名の代表的なものは以下の通りです。

  • EUC-JP
  • UTF-8
  • ISO-2022-JP
  • Shift_JIS

文字エンコーディング名を出力する際には、大文字と小文字の差異は同一視されますが、ハイフンの有無などのちょっとした誤記であってもIEは正しく文字エンコーディング名を認識できなくなるので、注意が必要です。

meta要素でcharsetを指定しているときに、meta要素より前に攻撃者が文字列を挿入可能

IEが正しく解釈できる文字エンコーディング名を出力している場合でも、HTTPレスポンスヘッダではなくmeta要素内で出力しており、さらにそのmeta要素より前方に攻撃者が任意の文字列を出力できる場合には、IEがコンテンツをUTF-7と解釈してスクリプトが動作することになります。

HTTP/1.1 200 OK
Content-Type: text/html

<html>
<head>
  <title>+ADw-/title+AD4APA-script+AD4-alert(1)+ADw-/script+AD4-</title>
  <meta http-equiv="content-type" content="text/html;charset=Shift_JIS">
</head>
...

たとえば、このようなコンテンツにIEでアクセスした場合、meta要素内での文字エンコーディングの "Shift_JIS" という指定よりも前方にあるtitle内のテキストから、コンテンツをUTF-7と解釈してしまいます。

まとめ

このように、Webアプリケーション側で意図した文字エンコーディング名とIEの解釈する文字エンコーディング名の不一致が、UTF-7によるXSSを引き起こしていることを理解していただけたと思います。

攻撃側は、少しでも文字エンコーディング名を制御できる隙があればIEにUTF-7と解釈させようとさまざまな手段を編み出しています。またUTF-7によるXSSは、実際に攻撃的なリクエストを発行しなくとも、正規の応答だけでもレスポンス内の文字エンコーディングの有無を見ることによってXSSする可能性がある、ということを攻撃側は効率よく把握することができます。

前回の繰り返しになりますが、UTF-7によるXSSへの対策としてはHTTPレスポンスヘッダにて

Content-Type: text/html; charset=UTF-8
Content-Type: text/html; charset=Shift_JIS
Content-Type: text/html; charset=EUC-JP

のいずれかを出力するというシンプルなルールで完全に防ぐことができます。

本稿が少しでもみなさんのセキュアなWebアプリケーション作成の手助けとなれば幸いです。

おすすめ記事

記事・ニュース一覧