続: そろそろUTF-7について一言いっとくか

史上空前のEUC-JPブームはとりあえずおいておいて、今日も最強の文字コードであるUTF-7について。
これまで私の中では、UTF-7によるXSSを避けるためには、Shift_JISUTF-8といった、IEが受け入れ可能なcharsetをHTTPレスポンスヘッダまたは<meta>で明記してやればよいという理解でした。
具体的には、HTTPレスポンスヘッダで

Content-Type: text/html; charset=Shift_JIS

とするか、生成するHTML内で

<meta http=equiv="Content-Type" content="text/html; charset=Shift_JIS">

とすれば、UTF-7によるXSSは防げると思っていました。ところが、後者の<meta>によるcharsetの指定では、条件によってXSSが防げないことがあるということに気付きました。
その条件とは

  • HTTPレスポンスヘッダではcharsetを指定していない
  • <meta>より前に<title>...</title>を出力している
  • <title>...</title>の内容を攻撃者がコントロール可能

という場合です*1

<html>
<head>
  <title>+ADw-/title+AD4APA-meta http-equiv+AD0-content-type content+AD0-text/html+ADs-charset=utf-7+AD4-</title>
  <meta http-equiv="content-type" content="text/html;charset=Shift_JIS">
</head>

このようなHTMLを生成している場合、本来の<meta>よりも前に、<title>内の文字列からUTF-7であると判断され、攻撃者の仕込んだ偽の <meta> が有効になります。そのため、攻撃者はUTF-7を使って任意のスクリプトを注入できる可能性があります(実際の動作サンプル)。

また、偽の <meta> を注入するまでもなく、<title> 内の文字列をみて UTF-7 と判断されているので、以下のような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>

UTF-7によるXSSを避けるためには、IEが受け入れ可能な文字エンコーディング名をHTTPレスポンスヘッダで指定する、ということを心がけるようにしましょう。<meta>で文字エンコーディングを指定する場合は、<meta> よりも前の部分に攻撃者がコントロール可能な文字列を置かないように気をつけましょう。前者のほうが簡単なのと、後者では(まだ書くことができない)とある問題が発生する可能性がありますので、HTTPレスポンスヘッダでの出力をお勧めします。

*1:<title>以外でも攻撃者が<meta>より前に文字列を挿入可能なら同様に攻撃が成功するが、現実的には <title> 以外の挿入は少ないと思う