8.5. 出力時に文字符号化を制御する

一般的に、安全が必要なプログラムでは、そのプログラムが決めた前提条件と クライアントが同期していなければいけません。 よくある問題の 1 つとして、文字を出力する際に符号化指定を行わないと、Web アプリケーションに支障がでる点が挙げられます。 すべてのデータが信頼できる元からくる場合はかまいません。しかし、信頼できない 元からもデータがくるなら、信頼できない元が安全が必要なプログラムの予期でき ない別の符号化を使って、データを偽るかもしれません。 これがサイトにまたがる悪意ある攻撃のきっかけになります。詳しくは、 Section 4.9 を見てください。

CERT's tech tip on malicious code mitigation でこの文字符号化を指定しない 問題についてかなりわかりやすく解説していますので、ここで引用します。

Web ページの中には、文字符号化(HTTP の「charset」パラメタ)を定義していない ところがたくさんあります。 HTML や HTTP の初期バージョンでは、文字符号化が定義されていないと、デフォルト で ISO-8859-1 であると仮定していました。 実際に、ブラウザのデフォルトはさまざまだったので、デフォルトを ISO-8859-1 としてしまうのには無理がありました。 HTML version 4 では、文字符号化を指定していなければ、どの符号化を使っても かまわないことになりました。

Web サーバがどの文字符号化を使うのか指定していないと、どの文字が特殊文字 なのかがわかりません。 文字符号化を指定していない Web ページは、たいていの場合うまく動作します。 それは文字集合のほとんどが、128 以下のバイト値に同じ文字を割り当てて いるからです。 しかし、128 以上の値のどれが特殊文字なのでしょうか。 16 ビットの文字符号化方式には、「<」のような特殊文字を表示するのに、追加で 複数バイトを使っているものもあります。 ブラウザには、これを別の符号化として認識し、動作するものもあります。 これは「正しい」動作なのですが、悪意あるスクリプトを使った攻撃が、防ぎづらく なってしまっています。 サーバはどのバイトシーケンスが特殊文字を現すのか、単純には判断できなくなり ます。

たとえば、UTF-7 は「<」と「>」に対して、異なる符号化を提供しています。 またよく使われているブラウザのいくつかは、これらをタグの開始と終了として認識 するものもあります。 これはブラウザのバグではありません。 文字符号化が本当に UTF-7 なら、これは正しい動作です。 問題は、ブラウザとサーバが符号化で同期がとれていない状況に陥る可能性が ある点です。

この問題を説明するのは厄介ですが、有り難いことに、HTML での解決策は簡単です。 HTML ヘッダに文字セットを下記の例のように設定するだけです。
<HTML>
<HEAD>
<META http-equiv="Content-Type"
content="text/html; charset=ISO-8859-1">
<TITLE>HTML SAMPLE</TITLE>
</HEAD>
<BODY>
<P>This is a sample HTML page
</BODY>
</HTML>

技術的な観点からすると、文字符号化を HTTP プロトコルの出力の一部として設定 する方が、さらに良い解決策です。しかし、これを難しくしているライブラリが存在 します。 この解決策は技術的には優れています。クライアントに強制的にヘッダーを調べさせ、 ヘッダー中にあるMETA 情報を読んでわかる文字符号化を判定する必要がないからです。 もちろん、現実的には上記のような META 情報を読めなかったり、正しく扱え なかったりするブラウザは、市場では受け入れられていません。しかしそれは別問題 です。 いずれにしても、サーバが HTTP プロトコルの一部として「文字セット」に適切な 値を設定して送り出す必要があります。 残念ながら、この(技術的には優れた)解決方法を心からお薦めできません。 それは、古い HTTP/1.0 対応のクライアントには、明示的に指定してある charset パラメタを適切に扱えないものがあるからです。 HTTP/1.1の仕様では、クライアントがこのパラメタに従うように定めていますが、 それを実現するのは、ははなはだ疑わしいと思います。おそらく、正しい文字符号化 の利用を強制する手段として、その仕様を唯一の方法としてではなく、追加の方法 として使うことになるでしょう。