FireFoxのロケーションバー

FireFoxのロケーションバーに

http://www.google.com/search?q=諸葛亮

と入力すると

http://www.google.com/search?q=%8F%94%8A%8B%97%BA

Shift_JISエンコードされる*1。同様に

http://www.google.com/search?q=諸葛靚

と入力すると

http://www.google.com/search?q=%E8%AB%B8%E8%91%9B%E9%9D%9A

エンコードされた。「諸葛」で一致するはずの冒頭部分のコードが違う。UTF-8エンコードされているのだ。おそらくFireFoxは、まずShift_JISでのエンコードを試み、それに失敗してから*2UTF-8エンコードするのだろう。

これは、かなりウザい。

こういう挙動の曖昧さってセキュリティ的にどうなの?と思ったりした。

PHPのurldecode関数にそれぞれの文字列を渡すと、前者はShift_JISで「諸葛亮」、後者はUTF-8で「諸葛靚」が得られる。こちらもやはり、どちらか一方でのデコードを試み、それに失敗してから他方のデコードを試みているのだろう*3Shift_JISUTF-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

FireFoxWikipedia

なんでこういうことを書いたかっていうと、【Wikipedia】みたいにURLの一部として渡されたクエリ文字列をキーにデータベースからデータ引っぱってくるような仕組みを作りたかったんだけど、実際テストしてみたらIEOperaでは問題なかったのにFireFoxで試したときだけ検索に失敗したので、おかしいなと思って調べたら、FireFoxのときだけ文字列がShift_JISで渡されたのに気付いたというわけなんですね。あたしゃてっきりどのブラウザでもUTF-8でしか渡されないと思いこんでたよ。

それじゃWikipediaではどういう扱いをしてるんだろうと思ってMediaWikiのソースをダウンロードしてみても、どうも文字コードを変換してるらしいところは見られない。おっかしーなーと首をひねった後で気がついた。

FireFoxではWikipedia使えねぇじゃん。

FireFoxのロケーションバーに入力された文字列はShift_JISエンコードされるから、UTF-8の文字列をキーにしてるWikipediaでは当該記事を見つけられず、記事がないよと言われてしまう。オプションを開いても標準エンコードUTF-8に変える選択肢のないFireFoxもダメぽだし、UTF-8前提でShift_JISを受けつけないWikipediaもダメぽだし、ダメとダメがぶつかってダメダメになっちゃった。

などとちょっと腹立ちまぎれに検索してみたら、「ツール→オプション→コンテンツ→フォントと配色→詳細設定→規定の文字エンコーディング」に設定があるらしい。深えよ!しかもフォントの問題じゃねえよ!しかしようやく「諸葛靚」が検索できるようになった…と思ったら「諸葛亮」だとエンコードに失敗して検索できねえよ!なんでかと思って調べてみたら、「諸葛靚」だとUTF-8で渡されて、「諸葛亮」だとShift_JISで渡されてる。またしてもお前か…!つねにUTF-8で送信してくれよ!*6

FireFox+Wikipediaでぐぐっても、関連の話題はあんまり出てないみたいだし*7FireFoxのバージョンの問題かもしれないね。少なくとも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));

素直にこうやった。UTF-8文字列をUTF-8文字列に変えてるのが明示的になって、あんまり気分はよくないけど。

うーん、やっぱりダメだ。

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としてはありえないはちゃめちゃコードに壊れてしまい、失敗する。

もう、イヤんなってくる。これというのも、FireFoxPHPが、IBM拡張を中途半端にカバーしているせいだと思うよ。頼むから、IBM拡張なんかサポートしないでくれ……。orz

*1:Googleが内部でエンコードしたのをFireFoxに返しているのではなく、FireFoxGoogleに渡す前にやっている。

*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辞書とか。しかし、それにしても与えた文字列によって挙動を変えるのはどうなんよ、ってのは疑問として残る。