読者です 読者をやめる 読者になる 読者になる

Lambdaカクテル

ソフトウェア開発者です.玉石混淆です.

2ちゃんねる専用ブラウザが重大?なHTTP/HTMLの規約違反をしているかもしれない

ども。GitHubで発生したIssueをとりあえずまとめることにしました。

概要

P2Pの実験で2ちゃんねる専用ブラウザ(以下専ブラ)とPlay! Frameworkを通信させているのですが、問題が発生しました。ある種の専ブラからの書き込みが文字化けするらしい(リンクはGitHubのIssue)のです。

結論から申しますと、2ch専ブラは正しくPOSTメソッドのパラメータをエンコードしていません。

パーセントエンコーディング

まず、「2ちゃんねる」への投稿はHTTP POSTメソッドを利用して行います。POSTメソッドのパラメータはパーセントエンコーディング(URLエンコードだとかURLエスケープとも呼ばれる)されていることが必要です。このあたりの基礎的な解説は、有名な「と〜く2ちゃんねる」を参照していただけるといいと思います。要するに、こういう -- 言語道断横断歩道 -> %8c%be%8c%ea%93%b9%92f%89%a1%92f%95%e0%93%b9 -- 文字列のことです。

さてここで厄介なのが、(Wikipediaを見る限り)パーセントエンコーディングには二種類ある、という事です。RFC3986のSection 2.1による「狭義のパーセントエンコーディング」と、RFC1866のSection-8.2.1による「application/x-www-form-urlencoded」というMIMEで指定される符号化の方式です。そして、POSTに利用されるのは後者の方式です。これはHTML4.01の規約に記載されています。

If the method is "post" and the action is an HTTP URI, the user agent conducts an HTTP "post" transaction using the value of the action attribute and a message created according to the content type specified by the enctype attribute.


application/x-www-form-urlencoded

This is the default content type. Forms submitted with this content type must be encoded as follows:

Control names and values are escaped. Space characters are replaced by `+', and then reserved characters are escaped as described in [RFC1738], section 2.2: Non-alphanumeric characters are replaced by `%HH', a percent sign and two hexadecimal digits representing the ASCII code of the character. Line breaks are represented as "CR LF" pairs (i.e., `%0D%0A'). The control names/values are listed in the order they appear in the document. The name is separated from the value by `=' and name/value pairs are separated from each other by `&'.(強調は筆者による)

二つの符号化の方式の違いは、半角スペースの扱いです。「狭義のパーセントエンコーディング」では、半角スペース、つまりASCIIコード0x20は"%20"としてエンコードされますが、「x-www-form-urlencoded」では半角スペースは"+"エンコードされます。

  • 狭義のパーセントエンコーディング: 半角スペースは"%20"。
  • application/x-www-form-urlencoded: 半角スペースは"+"。HTTP/POSTに使用される。

Playの場合 - java.net.URLEncoder

さて、Play! FrameworkでHTTP/POSTパラメータのデコードに利用されているのはjava.net.URLEncoderです。このクラスは規約に忠実にapplication/x-www-form-urlencodedを適用し、半角スペースを"+"としてエンコードします。

専ブラの場合

問題なのは専ブラです。パケットキャプチャなどにより確認・報告した限りでは

  • Thousand (Mac OSX)
  • navi2ch (Emacs)
  • JaneDoe Viewer (Windows)

少なくともこれらの専ブラが「狭義のパーセントエンコーディング」を使用し、半角スペースを"%20"としてエンコードしているようです。さらに、これらのクライアントはContent-Typex-www-form-urlencodedを指定しながら、「狭義のパーセントエンコーディング」を利用しているようです。

しかし一定数の専ブラが、この奇妙な実装をしているということは、もしかしたら2ちゃんねるがこの方式を強制しているのかもしれません。正確なapplication/x-www-form-urlencodedを利用して2chに書き込みできるかどうかは確かめていませんが、余裕があれば検証してみたいと思っています。


実証

Firefox

さて、手持ちのブラウザ(User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:25.0) Gecko/20100101 Firefox/25.0\r\n)でシベリア超速報に書き込みしてみたところ、半角スペースはきちんと"+"としてエンコードされ、Content-Typeはapplication/x-www-form-urlencodedであり、正常に書き込まれていました。

専ブラ

そして、手持ちの専ブラ「Thousand」で同じように書き込みしてみたところ、Content-Typeがapplicatioin/x-www-form-urlencodedであったにもかかわらず、半角スペースは"%20"としてエンコードされていましたが、正常に書き込みされていました。


おそらく2ちゃんねるではどちらのエンコーディングもサポートできているようです。

しかしながら、規約に従わない専ブラがあるのは問題だと思っています。どのように実装しているのかは私の知る所ではありませんが、規約は規約ですので従うべきではないでしょうか。正しい application/x-www-form-urlencodedを使用しても問題は無いのですから。

どうか識者の方、ご意見ください。