locale_accept_from_http

locale_accept_from_http

[2] PHPlocale_accept_from_http, Locale::acceptFromHttpHTTP Accept-Language: ヘッダー値から適当なロケールを返すものです。

[3] HTTPAccept-Language: ヘッダー値っぽい感じの入力を与えると、 何かロケールIDっぽいものが返ってきます。

[4] HTTPAccept-Language: は簡単そうに見えて実は複雑な構文を持っているので、 HTTP に精通していないと間違って実装してしまいがちです。 だから一般の開発者がいちいちそれを意識しないで処理できる機能を提供しようという言語開発者の判断は正しい。 自分でやらないで言語機能を使おうと判断したアプリケーション開発者の判断もまた正しい。

[5] でもそこに落とし穴が潜んでいて、なんかいい感じにやってくれるわーというフレームワークの機能は往々にしてなんとなくいい感じで動いてそうだわーという雰囲気だけ提供してくれて、 あちことに罠が設置されてたりするんですよねえ。これがその実例。

[6] ドキュメントにはロケール識別子が返されると書いてありますが >>1、 それがどんなものなのかはよくわかりません。 ドキュメントを隅から隅まで読み込めばどこかに書かれているのかもしれませんが、 少なくてもこのページ >>1 には書いてありません。 このページに唯一あるのは返ってくる値の例 en_US>>1、 ここから POSIXロケール識別子的なものが得られるのかなあ、 という感触がありますが、それが正しいのかどうか。

[7] 公式ドキュメントの例示は $locale という変数に結果を代入していますけど、 巷で流通している利用例だと $lang という変数に代入していることもあったりします。 つまりロケール言語がわからない人もこれを使っているということ。 そりゃあわからないですよね、公式ドキュメントにも何も書いてないもん。 Accept-Language (言語) からロケールを取得する、 という少々わかりにくいことしてるのに、説明がないと混乱する人が出てくるのは必然でしょう。

[8] あとこれ、ヘッダー値に入ってるものをただ返すのではなくて、 どれか一番使えるものを返してくれるらしいです。 >>1 てきとーな値を入れて渡しても、そのまま返ってくるわけではなくて、 使えるものを選んでくれるそうな。 でもそれってどうやって選ぶんでしょうか? 引数はヘッダー値1つだけですよ。 その肝心なところがドキュメントに全然書いてないのが謎。 隅から隅まで探せばどこかにあるのかもしれませんけど、 少なくてもこのページには書いてなくて。 (ユーザーコメントには、指定できないって書いてあるw)

[10] ソースコードを見ると、利用可能なロケールの一覧を用意して、それと一致するものが返されてくるみたいですねえ。 そしてその一覧は直接指定することはできないっぽい。

[11] だから利用可能な一覧に jaja_JP が(たまたま)入っていたりして、 Accept-Language の値次第で ja が返ってきたり ja_JP が返ってきたりするんですねえ。 そんなことはドキュメントに書いてないので、 いろんなブラウザーがいろんな値を送ってくるのを経験しないとわからないわけです。 これはバグの温床っすな。

[12] ちな、 Accept-Language:IETF言語タグなので、 Accept-Language: ja-JP が指定されるとロケールIDとして ja_JP が返ってくる。これは罠。

[13] 利用可能なロケールの一覧はどこからやってくるかというと

ures_openAvailableLocales という ICU関数が呼び出されてます。

ICU の仕様によって PHP の挙動が変わるって、そんなことちゃんと理解してWebアプリケーション作ってる開発者いるのかなあ? この一覧をちゃんと思った通りのものにするにはどこのどのファイルをいじればいいんだろう?

[16] では肝心の Accept-Language からロケール識別子への変換はどうやっているのかというと、

これもやっぱり ICUuloc_acceptLanguageFromHTTP を使っている模様。

[17] てことは返ってくる結果はUnicodeロケール識別子POSIXロケール識別子ぽい味付けのやつなんかな?

[19] Accept-Language: というのはつまり外部入力なわけで、 どんな入力が与えられるかわからないからちゃんと処理しないとセキュリティー的にやばいんですが、

こういう脆弱性の報告が実際にあって、与えられた Accept-Language:文字列が長すぎるときにいかれてました。

この修正は PHP 側でなされてるようですけど、 Accept-Language: なんて10中8,9どころか9.999くらい外部入力に決まってるんですから、 そもそも ICU がちゃんとしてないと駄目な案件なんですよね。

で、 PHP というかおおよそ WAF はそういう不正な入力が来たらどうするかというのを常に考えないといけない性質のソフトウェアで、 外部ライブラリーを使うときもそれがその意味で安全かどうか評価して決めないといけない。 それが出来てないという意味では PHP もあかんのです。

[20] ICU ってあの天下の IBM の作ったライブラリーで今は Unicode Consortium が仕切ってて、なんだか立派なちゃんとしたソフトウェアなんじゃないか、 って漠然としたイメージがあるんじゃないかと思いますけど (その分なんとなくとっつきにくいイメージもありますけど)、 実態はこのレベルなんで、そのままWebアプリケーション開発に使っちゃうとあとで泣きを見るやつですね。

[21] Webアプリケーション開発には Web をちゃんと熟知してる技術者が作ったソフトウェアを使いましょう。

[22] ってそれ PHP は・・・