2015年4月24日金曜日

WebSocket プロトコルの話


HTML5 で標準化された WebSocket は、Webサーバ(アプリケーションサーバ)とクライアント(主にブラウザ)との双方向通信を実現するものです。一度接続をするとサーバサイドからも好きなタイミングでデータを送ることができます。近頃のブラウザであればほぼ実装されています。(実装されていないブラウザはあるのか。。)

      プロトコルの仕様は RFC6455 として標準化されています。
 
WebSocket は 基本 80 番ポートの TCP 通信であるので、一度接続ができてしまえば任意のタイミングで NATを越えて の送受信が可能であることを意味します。プラグインを使わずに、ブラウザだけで実現できるようになったことで Web アプリの可能性が広がりました。

データフレームはヘッダとペイロードで構成され、可能な限り小さくまとめられています。ヘッダ長は最小の場合2バイト、最大でも14バイトであるので、HTTP のものと比べると劇的に小さいと言えます。

ヘッダが小さい上に、データ更新のための無駄なポーリングを抑えられるので、WebSocket を利用することでサーバサイドの負荷を大幅に抑えることができます。


データフレーム仕様:

  ※ 単位: ビット



オペコード  ( opcode )

operation code の略で、何のフレームなのかを示すコードが指定される。次の種類があります。

オペコード 意味 備考
0 継続フレーム   大きなペイロードを分割して送る際に使用
1 テキストフレーム   ペイロードがテキスト(UTF-8)
2 バイナリフレーム   ペイロードがバイナリデータ
8 クローズフレーム   コネクションを切断する際にお互いに投げ合う
9 ピンフレーム   キープアライブ用
10ポンフレーム   ピンフレームの応答


ピンフレーム/ポンフレーム  ( Ping-Pong )

通信相手が応答可能な状態なのかを確かめるために使用されます。ピンフレームを受信したら、できるだけ早くポンフレームを返信しなければなりません。

 ※ このやりとりが卓球(ピンポン)をイメージすることにその名が由来します

もし応答が不要な場合(一方向の確認)は、ポンフレームの送信だけで完結することができます。


クローズフレーム

切断する際はクローズフレームを送信しなければなりません。これを受信した側もレスポンスとして、同じクローズフレームを返信しなければなりません。

ブラウザについては、JavaScript から WebSocket の接続をしますが、そのページから離れてしまうと、あるいはページリロードをしたタイミングで、自動的にクローズフレームを投げて切断されるように実装されていました。

モバイル端末やPCがスリープモードに入ると、コネクションは切断されます。

ペイロード長 (9ビット目から最大79ビット目まで)

ペイロードの長さを設定する。ペイロードの長さによって、「ペイロード長」を設定する領域やその長さが変わるのが少しややこしい(詳細は省略)。

ヘッダの長さを抑えるためにそのような仕様になったと考えられます。

ペイロード

実際のデータ本体。
ペイロードにどんなデータを載せるかは、アプリケーション次第。フォーマットはプレーンテキスト、JSON、XML、MessagePack、独自仕様など、なんでもあり。サーバサイドとクライアントサイドで仕様を合わせれば良いです。

ペイロードがテキストかバイナリか、適切な値をオペコードに設定しましょう。

マスク処理

マスクフラグが1の場合に、マスキングキー(Masking-key)でペイロードデータを変換します。クライアントからサーバに送る場合はマスク処理を施さなければなりません。マスキングキーにはランダムな値が使われます。

ちなみに、ブラウザではこれらが実装されているので、開発者はほとんど気にする必要はありません。


なぜ、わざわざ負荷のかかるマスク処理をするのか

マスキングキーが同じフレームに載っているので、暗号化が目的でないことは分かります。

マスク処理がないと、WebSocket の中継点にあるプロキシのキャッシュを汚染させることで、悪意のあるスクリプトを実行させられる攻撃が成立する可能性があるようです。この攻撃を困難にするために、クライアントが送信する場合にマスク処理が行われます。

要は、WebSocket がネット攻撃のベースにならないようにするためなのです。



0 件のコメント:

コメントを投稿