WebSocketを使ってゴニョゴニョしているのだが、handshakeの失敗時のエラーをどう扱うべきなのか、よくわからない。WebSocket Protocol の仕様と WebSocket API の仕様で書いてあることが違う。
protocol仕様を読む限り、サーバーはhandshakeを拒否する場合適切なHTTPエラーコードを返さなくてはいけない、と書いてある。が、クライアント側のウェブブラウザ上のjavascriptでそれを期待しているとエラーイベントからはHTTPエラーコードは受け取れない。WebSocketの異常終了コード1006が固定でセットされている。
そこでAPI仕様を読むと、これらを行わない理由が赤字で大きく書いてある。
- オープンリダイレクタが設置されたサイトで脆弱性になるので、handshakeのレスポンスはHTTPとして扱わない。
- 悪意あるスクリプトに攻撃の準備としてネットワーク環境の調査に使われるのを防ぐため、接続失敗の理由をスクリプトに通知してはいけない。
接続失敗の理由を開示してはいけない、というのは特定のシチュエーションの場合、と限定してあるのだが、この一文
A server that did not complete the opening handshake (e.g. because it was not a WebSocket server).が示す範囲がよくわからなくて、これがもし403とか返した場合も該当するなら、サーバーはHTTPエラーコードを返さなくてはいけないがブラウザはエラーコードを握りつぶさなくてはいけない、ということになる。実際API仕様にもHTTPステータスを受け取れるとは書いてないし、そういうもんだと言われればそれまでだが、実際のところ困る。
chromeのコードもはっきりそうなっているっぽくて( @gtk2k さんに教えていただきました。ありがとうございます。 )、101以外のHTTPステータスを受け取っても、特に個別の動作はしないし、エラーイベントのどこを探してもHTTP statusが見つからない。リダイレクトもしない。
Unexpected response code: 403という文字列がエラーコンソールに出てくるけど、javascriptからは拾えなそう。
一体どうすれば良いのかわからず、原本 http://tools.ietf.org/html/rfc6455 および http://www.w3.org/TR/websockets/ と、そこから辿れる旧バージョンを参照した。以下の様な時系列で変更が行われた模様。
WebSocket Protocol hybi-05以前
プロトコル仕様に以下の記述がある。
- 記述1 ユーザーエージェントは、一部のシチュエーションではエラーの理由をスクリプトに伝えてはいけない
- 記述2 handshakeのレスポンスとして101以外を受け取った時、WebSocket接続を終了する
User agents must not convey any failure information to scripts in a way that would allow a script to distinguish the following situations:http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-05#section-7.1.1
If the status code received from the server is not 101, the client MUST fail the WebSocket connection.http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-05#section-5.1
WebSocket Protocol hybi-06 February 25, 2011
プロトコル仕様から記述1が消える
ietf のメーリングリスト 28 Mar 2011
プロトコルレベルではHTTPをサポートするべきという方向に話が進む
(超意訳)これ(オープンリダイレクタの問題)って俺達の問題じゃなくてWHATWGの問題だよね。リダイレクトや認証機能が無しなんてやってらんないから入れちゃおうぜ。もしブラウザが当面リダイレクトをサポートしないって事になってもOK。後でブラウザ側の挙動だけ変えれば済むなら楽だし。
I don't think it is "our problem". I think it is a "W3C/WHATWG HTML-5 WG problem". This is essentially an API issue for the browser websocket object. There are plenty of ways around this (adding optional follow redirects, exposing redirect responses etc. etc.), but I think it is the W3C and WHATWG HTML-5 working groups that should be where such matters are decided. As a web developer I really rather not have to go without redirection and other common auth methods - and I take responsibility for my servers not being open redirectors..... but would understand if the W3C/WHATWG decided not to support these features - at least initially. However, we should make the protocol such that if/when the API does support these features, then it will only be an in browser change and not an update of the protocol.http://www.ietf.org/mail-archive/web/hybi/current/msg06954.html
WHATWG のメーリングリスト Mar 29 2011
APIレイヤでHTTPを理解するかは先送りの方向に
(超意訳)(HyBiのスレッドではHTTP使う方向になってるぜ、という話を受けて)オープンリダイレクタの問題はセキュリティ上の大穴だという意見に同意だ。httpが犯した過ちとそこで起こった問題を繰り返すべきではない。この問題は先送りにして、当面APIレベルではリダイレクトには対応しない、という方向で。
I absolutely agree that redirects are a big source of security problems. If we are going to support them for websocket then I think we need to learn from the mistakes of http as to not repeat the problems that occurred there. (中略) But I'm totally fine with punting on this for the future and just disallowing redirects on an API level for now.http://lists.whatwg.org/htdig.cgi/whatwg-whatwg.org/2011-March/031079.html
WebSocket Protocol hybi-07 April 22, 2011
記述2部分が変更 101以外はHTTPとして処理するIf the status code received from the server is not 101, the client handles the response per HTTP procedures. Otherwise, proceed as follows.http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-07#section-5.1
WebSocket API r1.210 May 24, 2011
旧記述2相当の内容がAPI仕様に引越し
Block redirects in WebSockets (whatwg r6148)+ When the user agent validates the server's response during + the "establish a WebSocket connection" algorithm, if + the status code received from the server is not 101 (e.g. it is a + redirect), the user agent must fail the websocket + connection.http://dev.w3.org/cvsweb/html5/websockets/Overview.html#rev1.210
WebSocket Protocol hybi-08 June 7, 2011
handshake失敗時にサーバーはエラー理由をHTTP error codeで表現しなさい、という記述が登場
If the server does not wish to accept this connection, it MUST return an appropriate HTTP error code (e.g. 403 Forbidden) and abort the WebSocket handshake described in this section.http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-08#section-5.2.2
WebSocket Protocol RFC6455 PROPOSED STANDARD December 2011
現在のhandshakeのレスポンスの扱い protocolバージョン
Any status code other than 101 indicates that the WebSocket handshake has not completed and that the semantics of HTTP still apply. The headers follow the status code.
WebSocket API r1.271 Jul 11, 2012
記述1相当の内容が一年五ヶ月ぶりにAPI仕様として復活。
Clarify what codes are exposed in case of error, since this text was mysteriously removed from the RFC at some point. (whatwg r7175)+ User agents must not convey any failure information to scripts + in a way that would allow a script to distinguish the following + situations:http://dev.w3.org/cvsweb/html5/websockets/Overview.html#rev1.271
WebSocket API W3C Candidate Recommendation 20 September 2012
現在のhandshakeのレスポンスの扱い APIバージョン。 赤くて目立つようにHTTPは混ぜるな危険とwarning。
When the user agent validates the server's response during the "establish a WebSocket connection" algorithm, if the status code received from the server is not 101 (e.g. it is a redirect), the user agent must fail the WebSocket connection. Following HTTP procedures here could introduce serious security problems in a Web browser context. For example, consider a host with a WebSocket server at one path and an open HTTP redirector at another. Suddenly, any script that can be given a particular WebSocket URL can be tricked into communicating to (and potentially sharing secrets with) any host on the Internet, even if the script checks that the URL has the right hostname.
というわけで、プロトコル仕様ではhandshakeのレスポンスはHTTPとして扱う方向になっているが、API仕様ではこれを無視する。将来的に統一するならAPI仕様が変更される。というのが現状のようです。接続失敗の理由をスクリプトから隠す件については結局良くわかりませんでした。MLはgoogle先生が最初に教えてくれた周辺しか読んでないので、他にも色々議論があったのかもしれません。
実際どう対処すればよいか。少なくとも現時点のブラウザの実装がエラーコードを隠蔽してしまう以上、サーバー側の実装をするときにhandshakeの結果をHTTPで返しても無駄な気はする。handshakeしてから即closeするか、またはエラー診断用のHTTPのエンドポイントを用意するとか。うーん。