【PHP関連】「&」(アンパサンド)をエスケープしなければならない実例
ここ最近、htmlspecialchars関数について調べていて、いろいろ記事にしていたのですが、コメントをいただいた。参照となるページを教えていただいたのですが、実はそのページは、この記事を書く前に既に読んでいました。コメントに書かれていたページのURLをここに記させていただくと、
●「サニタイズ言うなキャンペーン」私の解釈(2006/11/29)
http://kmaebashi.com/zakki/zakki0042.html
です。で、私の文章の書き方が悪かったのかもしれませんが、私はこの方の解釈とほぼ一緒(のつもり)です。ただ、このページで例として挙げられている「Taro&Jiro's castle サウスポール」の「&」は別に「&」とエスケープしなくても「Taro&Jiro's castle サウスポール」と表示されます。
そのため、そもそも「&」を「&」とエスケープしなければならない場合とは何かということを考えていました。その実例がやっと見つかりました。今回いただいたコメントをきっかけに気づいた事例もあるのですが(コメントを下さったshiさん、ありがとうございます。)、それは後述するとしまして、まず、下記をご欄ください。
あいうえお&rlo;こけくきか
上の文字列は、「あいうえおかきくけこ」と見えると思いますが、ソースは「あいうえお&rlo;こけくきか」となっています。
「&rlo;」はUnicodeの制御文字で、文字列の方向を変えるためのものです。アラビア語などでは右から左に文字を書くので必要ということらしいです(昔の日本語も確かそうだった?)。
この「&rlo;」(数値参照文字なら「‮」)が出現すると、後続の文字列がおかしくなる(全部ではなく、一定のパターンでストップがかかるみたいです。)ので、
http://live.2ch.net/dome/kako/1010/10104/1010445506.html
のような現象が起きます。この2chの「&rlo;」騒動は2002年当時からあったみたいですね。今頃知って、お恥ずかしい。
「&rlo;」が入った場合にどれだけの後続の文字列を道連れにするかはまだ十分に検証できていませんが、HTMLソースのパターンによっては大規模な「表示化け」が起こりそうです。実際、Googleの検索結果でも、こちらのページはページタイトルや検索結果の件数が表示されている部分が、文字の順番が逆になっています。
「&rlo;」を入力され、「&」を「&」にエスケープしない場合(あるいは、htmlspecialchars関数を通した後、「&」を「&」に戻す処理を入れている場合)、ページの一部分の表示が乱れることももちろん問題ですが、
- 例えば、NGワードを決めているような掲示板やブログがあっても、それをスルーされてしまいます。「氏ね」をNGワードにしたいのに、「&rlo;ね氏」と入力されてしまうとチェックできまないことになります。
(--2007年12月31日午後17時13分頃の追記 start--)
でも、考えてみれば、「&」を許可してしまうと、「「氏ね」と書けば「氏ね」と表示されることになります。NGキーワード形式を取っているサイトでは「&」を許可してしまうと、数値文字参照方式での記述も同時にNGワードかどうかチェックして(10進数形式ではなく、16進数形式の「氏ね」でも駄目としなければなりません。)、NGワードに該当すればブロックできるようにプログラミングしておかないといけないので、なかなか面倒そうですね。
そういう意味でも、EUCの補助漢字を処理する場合、「&」については、「原則はタグとして実行されるのは禁止(=エスケープ処理)で、例外的なものも一部認める」というホワイトリスト方式にしないといけなさそうです。つまり、「htmlspecialchars関数の仕様は、実に理にかなっている。」ということになりそうです。
(--2007年12月31日午後17時13分頃の追記 end--)
- 掲示板やブログが荒れる元になりえます。そういうことをして喜ぶ連中に狙われると大変です。
- 別の脆弱性を突く攻撃との組み合わせなどで、何かあるかもしれません。(私は知らないだけで、何かあるのかもしれません。)
さらに、前述のように、今回コメントで書いていただいたページ(「サニタイズ言うなキャンペーン」私の解釈(2006/11/29))は既に読んでいたのですが、この記事を書くために、改めて読み直し、さらにHTMLソースを見させていただいて、あることに気づきました。文字参照の末尾のセミコロン「;」があってもなくても、ちゃんと表示されるんですよね。ということは、「Taro&Jiro's castle サウスポール」の場合は、「&」をエスケープしなくても問題ないのですが、「Taro©'s castle」の場合は、エスケープしないと「Taro©'s castle」になります。
私はセミコロンがないと動かないと思い込んでいたため、「Taro©'s; castle サウスポール」と入力して、「Taro©'s; castle サウスポール」のように表示されているかといって、「『Taro©'s; castle サウスポール』と表示されるべきなのでは? 」と文句を言う人がいたら、ただのクレーマーだと思っていたのですが、「;」なしでも動くなると、場合によっては、文字参照と解釈してほしくない場合だってあるかもしれないと思えてきました。
URLを紹介する場合に、パラメータに「copyright」というのがあって、これに「&」が付くと、
http://www.example.com/example.php?hoge=1©right=1
のようになります。この時、エスケープしないままURLがソース内にありますと、
http://www.example.com/example.php?hoge=1©right=1
のように、IE7やIE6 SP2では表示の際に「©right→©right」と解釈して、問題(Firefox 2.0.0.11やOpera 9.24では大丈夫)ということになりますから、ソースレベルでは、
http://www.example.com/example.php?hoge=1&copyright=1
と書いて、
http://www.example.com/example.php?hoge=1©right=1
と表示されるようにしなければなりません。以下、ブラウザの解釈によって違うようですが、
http://www.example.com/example.php?hoge=1&ampm=1
とソースの中で記述しなければ、
http://www.example.com/example.php?hoge=1&m=1
のようになります。「&m→&m」のように解釈されます。
http://www.example.com/example.php?hoge=1&regist=1
とソースの中で記述しなければ、
http://www.example.com/example.php?hoge=1®ist=1
のようになります。「®ist→®ist」のように解釈されます。
http://www.example.com/example.php?hoge=1&century=21
とソースの中で記述しなければ、
http://www.example.com/example.php?hoge=1¢ury=21
のようになります。「¢ury→¢ury」のようにIE6 SP2やIE7は解釈します。
ちゃんとエスケープしないと、
http://www.example.com/example.php?hoge=1×=1
と表示したいのに、
http://www.example.com/example.php?hoge=1×=1
にのように表示されることがあります。これらの文字が問題になりえます。
http://www.example.com/example.php?hoge=1¶=1
と表示されたければ、
http://www.example.com/example.php?hoge=1&para=1
とソースの中で記述しなければなりません。そうでなければ、「¶ → ¶」のようになります(これはIEだけでなく、Firefoxでもそうなるみたいです。Operaではなりませんでした)。パラメータの意味で「para」とかいう名称は使いたくなりそうですし、気をつけたほうが良さそうです。
下記のリンクにカーソルを合わせていただくと、IEやFirefox(2.0.0.11)では、リンク先URLが文字化けしていることが分かります。実際クリックすると、予期しないページに飛びます。
¶
「そんなの当たり前」と思われるかもしれませんが、私は「;」がなくても表示されることを知らなかったため、このような事例に今の今まで気づきませんでした。
ここ最近の記事を書くきっかけになった「EUCのページにおける補助漢字の処理」に関しては、結局のところ、どうしていいか分からないというのが実情です。「‮」対策を考えると「正規表現+例外処理」になりそうですが、これだと漏れが生じる可能性もあるかもしれません。
「&」のエスケープにより、「鷗」やフランス語などの一部外国語ぐらい(補助漢字ではありませんが、ハングルなどの一部外国語)しか実質・文字化けしないのであれば、下手に触らないで、今のまま放置しておいたほうが無難であるという感じに思えてきました。
もしくは、数千字ある補助漢字や、「©」「®」などの文字実体参照のうち、「わりと使う文字だけ」をピックアップして、変換テーブルを作成して対処するホワイトリスト方式がいいのかもしれません。一律にpreg_replace関数で変換し、例外処理もプラスするブラックリスト方式は何だか怖い気がします。
| 固定リンク
この記事へのコメントは終了しました。
コメント