J2EE仕様の問題について考える

Windows-31JUTF-7によるXSSの要因になることはよく知られるようになって来ましたが、依然としてMS932やWindows-31Jが使われるWebアプリケーションは生産され続けています。

その原因の1つはJ2EEサーブレット及びJSPAPIの仕様の欠陥にあるのではないかと最近思うようになってきました。

javax.servlet.ServletResponse や JSP の仕様では、

  1. 文字列→バイト列への変換に使用する変換テーブル名
  2. HTTPのContent-typeヘッダに埋め込んでブラウザに送信されるエンコーディング

の2つに同じエンコーディング名を使う事になっているので、
Shift_JIS で特定の文字が文字化けしてしまうのを防ぐために Windows-31J による変換を行う様に指定するには、Content-typeヘッダに埋め込むのエンコーディング名に Windows-31J を使うしか方法がありません。つまり、

<%@page contentType="text/html; charset=Windows-31J"%>

と書く事になります。そうすると、HTML本体のmetaの方にもこれと同じ

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

と書いてしまってUTF-7によるXSSや予期せぬ文字化けの要因となってしまいます。

ということで、どういう仕様だったらいいのか考えてみます。

案A:

WebLogic で実装されている charset-mapping のような仕様を標準にしてしまう。

    <charset-params>
        <charset-overwrite>
          <used-charset-names>
              <charset-name>Shift_JIS</charset-name>
              <charset-name>MS932</charset-name>
              <charset-name>Windows-31J</charset-name>
          </used-charset-names>
          <iana-charset-name>Shift_JIS</iana-charset-name>
          <java-charset-name>Windows-31J</java-charset-name>
        </charset-mapping>
      </charset-params>

この設定がある場合に used-charset-names にマッチするエンコーディング名が使用されていたら、Content-Type ヘッダには iana-charset-name で指定したエンコーディング名、内部での文字列→バイト列変換には java-charset-name で指定したエンコーディング名を使用する。

案B:

setCharacterEncoding(String ianaCharsetName, String javaCharsetName)
setContentType(String contentType, String javaCharsetName)

を導入し、@pageディレクティブに

internalEncoding="内部で使用するエンコーディング名"

を追加する。これにより、

<%@page
 contentType="text/html; charset=Shift_JIS"
 internalEncoding="Windows-31J"
%>

と記述すると内部的に

setContentType("text/html; charset=Shift_JIS","Windows-31J");

に展開されて、最初の引数からcharsetを指定した部分を切り出して

setCharacterEncoding("Shift_JIS","Windows-31J");

が呼び出される

今出来る対策について

こういう仕様案は当面実装されることはありえないので、実際の開発では今ある仕様の中で何とかする方法も考えないといけません。

とりあえずの対策
  1. setContentType pageディレクティブのcontentType にはJavaの内部エンコーディング名(Windows-31Jなど)を書く
  2. meta にはブラウザが認識できるエンコーディング名(Shift_JIS など)を書く

metaがhead要素の先頭(攻撃者が文字列を注入できる場所より前)に書いてあれば IE は meta の Shift_JIS を検出してくれるはず。

凝った対策

案A相当の機能を持った Servlet filter を実装する

中間に何かを挟むことが出来る場合(2009-09-21追加,09-23一部修正)

とりあえずの対策を施した上で、とりあえずの対策のContent-typeヘッダは
中間に挟んだもので書き換える(これが一番簡単かも)


多分まだ単なる思いつきなので、もっといい仕様案がすでに出ていたりするような気もする。