FireFoxのロケーションバー
FireFoxのロケーションバーに
と入力すると
と入力すると
とエンコードされた。「諸葛」で一致するはずの冒頭部分のコードが違う。UTF-8でエンコードされているのだ。おそらくFireFoxは、まずShift_JISでのエンコードを試み、それに失敗してから*2UTF-8でエンコードするのだろう。
これは、かなりウザい。
こういう挙動の曖昧さってセキュリティ的にどうなの?と思ったりした。
PHPのurldecode関数にそれぞれの文字列を渡すと、前者はShift_JISで「諸葛亮」、後者はUTF-8で「諸葛靚」が得られる。こちらもやはり、どちらか一方でのデコードを試み、それに失敗してから他方のデコードを試みているのだろう*3。Shift_JISとUTF-8でかぶっているコードがどうなるかは試してないので分からない。コードのかぶってる文字があるのかどうかも知らないし。
このurldecode関数だと、返り値のコードがShift_JISなのかUTF-8なのか分からないので、ちと困る。mb_parse_str関数を使うと、どのコードで渡してもかならずUTF-8で返ってくるので*4、いい感じ。これも内部で自動的に判断し、変換してるんだろう。
mb_parse_str($str, $array); $newStr = array_shift(array_keys($array));
セキュリティ的にどうかはまだ検討してない。
IEのアドレスバー
IEのインターネットオプションを開いて「常にUTF-8としてURLを送信する」のチェックを外して、「諸葛亮」を送信してみた。するとサーバ側にShift_JISで「諸葛亮」が渡されている。アドレスバーの表示は「諸葛亮」のまま。続いて「諸葛靚」を送ると、なんと、Shift_JISで「諸葛」の2文字だけが渡された。つねにShift_JISで送信しようとして、エンコードに失敗した文字はスキップされてしまうらしい。しかもアドレスバーは「諸葛靚」のままだ。ユーザに無断でクエリ文字を裏側で勝手に削除しちゃうって、どうなのよ。
Operaのアドレスバー
詳細設定で「すべてのアドレスをUTF-8でエンコードする」のチェックを外してみる。「諸葛亮」と入力すると「%E8%AB%B8%E8%91%9B%E4%BA%AE」とUTF-8でエンコードされた文字列がアドレスバーに入った。もちろん「諸葛靚」を入力しても「%E8%AB%B8%E8%91%9B%E9%9D%9A」でUTF-8だ。Operaがいちばんしっくりくる挙動だ。サーバ側ではそれぞれ「諸葛亮」「諸葛靚」とデコードされたUTF-8の文字列を受けとっていた*5。
FireFoxとWikipedia
なんでこういうことを書いたかっていうと、【Wikipedia】みたいにURLの一部として渡されたクエリ文字列をキーにデータベースからデータ引っぱってくるような仕組みを作りたかったんだけど、実際テストしてみたらIEやOperaでは問題なかったのにFireFoxで試したときだけ検索に失敗したので、おかしいなと思って調べたら、FireFoxのときだけ文字列がShift_JISで渡されたのに気付いたというわけなんですね。あたしゃてっきりどのブラウザでもUTF-8でしか渡されないと思いこんでたよ。
それじゃWikipediaではどういう扱いをしてるんだろうと思ってMediaWikiのソースをダウンロードしてみても、どうも文字コードを変換してるらしいところは見られない。おっかしーなーと首をひねった後で気がついた。
FireFoxのロケーションバーに入力された文字列はShift_JISでエンコードされるから、UTF-8の文字列をキーにしてるWikipediaでは当該記事を見つけられず、記事がないよと言われてしまう。オプションを開いても標準エンコードをUTF-8に変える選択肢のないFireFoxもダメぽだし、UTF-8前提でShift_JISを受けつけないWikipediaもダメぽだし、ダメとダメがぶつかってダメダメになっちゃった。
などとちょっと腹立ちまぎれに検索してみたら、「ツール→オプション→コンテンツ→フォントと配色→詳細設定→規定の文字エンコーディング」に設定があるらしい。深えよ!しかもフォントの問題じゃねえよ!しかしようやく「諸葛靚」が検索できるようになった…と思ったら「諸葛亮」だとエンコードに失敗して検索できねえよ!なんでかと思って調べてみたら、「諸葛靚」だとUTF-8で渡されて、「諸葛亮」だとShift_JISで渡されてる。またしてもお前か…!つねにUTF-8で送信してくれよ!*6
FireFox+Wikipediaでぐぐっても、関連の話題はあんまり出てないみたいだし*7、FireFoxのバージョンの問題かもしれないね。少なくとも2.0.0.4では上記のとおりでした。
うまく動かないぽ
mb_parse_str($str, $array); $newStr = array_shift(array_keys($array));
ローカル環境だとこれでうまく動くんだけど、リモートに載っけたらダメになった。内部エンコーディングの違いから来るんだろう。
$newStr = mb_convert_encoding($str, 'UTF-8', mb_detect_encoding($str));
うーん、やっぱりダメだ。
IBM拡張が含まれてなければ問題は起きないんだけど…。
FireFoxのロケーションバーに「鄧芝」を渡すと、「%FB%B9%8E%C5」とエンコードされた。IBM拡張を含めてはいるものの一応Shift_JISとしては理解できる形になっている。ところが、それをPHPに渡して
echo mb_detect_encoding($str, 'UTF-8,SJIS,EUC-JP,JIS,ASCII');
とやると、EUC-JPと判定されてしまう。もちろん、そんなでたらめコード判定ではUTF-8に変換することができない。
FireFoxのロケーションバーに「虞羡」を渡すと、「%8B%F1%FB%91」とエンコードされる。これもShift_JISとしては納得できる変換だし、そしてPHPでmb_detect_encodingさせると、正しくSJISと判定される。ところが、これをUTF-8に変換しようとすると、「%E8%99%9E%3F」などと、もうUTF-8としてはありえないはちゃめちゃコードに壊れてしまい、失敗する。
もう、イヤんなってくる。これというのも、FireFoxやPHPが、IBM拡張を中途半端にカバーしているせいだと思うよ。頼むから、IBM拡張なんかサポートしないでくれ……。orz
*1:Googleが内部でエンコードしたのをFireFoxに返しているのではなく、FireFoxがGoogleに渡す前にやっている。
*2:「靚」はJIS第二水準に含まれないので。
*3:mb_detect_encoding関数と同じことをやってるんだと思う。
*4:確認してないけど、たぶんmb_internal_encoding関数で設定されるコード。
*5:あれ、デコードされた文字列?よく考えるとそれもヘンだ。ApacheがやってるのかPHPがやってるのは知らんけど。
*6:あと「鄧艾」がShift_JISで渡されるのもマズい。「鄧」はJIS第二水準に含まれてなくて、いわゆるIBM拡張でむりやりShift_JISの空きに埋めこまれている。この文字ははてなでもいろいろトラブルを起こす。
*7:OpenSearchでのトラブルがこれと関係ありそう。
さらに検索するとFireFoxではなくMediaWikiの方に関連情報があった。主にFireFox側の問題なのに…。
http://ja.wikipedia.org/w/index.php?title=Help:%E3%83%A1%E3%83%87%E3%82%A3%E3%82%A2%E3%82%A6%E3%82%A3%E3%82%AD%E3%81%AB%E9%81%A9%E5%BF%9C%E3%81%99%E3%82%8B%E3%83%96%E3%83%A9%E3%82%A6%E3%82%B6#.E6.97.A5.E6.9C.AC.E8.AA.9EURL
ユーザにabout:configをいじらせなきゃWikipediaが使えないなんて、その時点でFireFox腐ってる。
いや、サーバに送信すべき文字コードは事前に分からないからFireFoxにそこまで求めるのは酷というものか。たしかにOperaみたいなやり方だとEUC-JPで受けつけてるサーバには文字列を送ることができないもんな。goo辞書とか。しかし、それにしても与えた文字列によって挙動を変えるのはどうなんよ、ってのは疑問として残る。