WiresharkでWebSocket通信をパケットキャプチャしてみた(テキストデータの送受信)

前回からの続き。
前回はコネクションを接続するときを覗いたので、今回はテキストデータを送受信するときのパケットを覗いてみる。

とりあえず、まずブラウザから"test"という4バイトのテキストを送信したときのパケットキャプチャを見てみる。
TCPヘッダなどを取っ払い、WebSocketの部分だけを抜き出して16進で表示したのが以下。

81 84 cc 2a af ce b8 4f  dc ba

詳細を確認していこう。
RFC6455の「5.2. Base Framing Protocol」を見るとフレームの構造はこんな感じらしい。

これを見ながら解析するのだが、実は深く考えないでもWiresharkならわかりやすく表示してくれる。

それぞれの意味は以下を参照。

Fin
0  分割されたデータの途中(まだ続きのデータがあるということ)。
1  最後のデータ。特に分割されなければ最初のデータが最後のデータになる。

Reserved
全てのビットは"0"でないといけない。"0"以外のビットがあると受信側でエラーとなる。
(ただし、接続時にここを使う拡張を両方のエンドポイントで合意できれば使うことも可能みたい)

Opecode
0000(0x0)  継続フレーム
0001(0x1)  テキストフレーム
0010(0x2)  バイナリフレーム
0011(0x3)  非制御フレーム用に予約済み
0100(0x4)  非制御フレーム用に予約済み
0101(0x5)  非制御フレーム用に予約済み
0110(0x6)  非制御フレーム用に予約済み
0111(0x7)  非制御フレーム用に予約済み
1000(0x8)  接続の切断
1001(0x9)  ping
1010(0xA)  pong
1011(0xB)  制御フレーム用に予約済み
1100(0xC)  制御フレーム用に予約済み
1101(0xD)  制御フレーム用に予約済み
1110(0xE)  制御フレーム用に予約済み
1111(0xF)  制御フレーム用に予約済み

Mask
0  Payloadデータがマスクされていない
1  Payloadデータがマスクされている。Masking-Keyにマスクしたときのキーが設定される。クライアントからサーバへ送信するデータは全てマスクされる。

Payload length
Payloadデータが何バイトあるかを示す。上の例では 100 となっているが、16進数の100は10進数の4なので、4バイトのデータということ。
(Payloadデータが126バイト以上ある場合は、さらに2〜8バイトの領域をExtended Payload lengthとして使い、Payloadデータの長さを表現する)

Masking-Key
Payloadデータをマスクするときに使用したキー。これはクライアントがランダムに設定した32ビットの値で、予測不可能なものとしなければならない。
Payloadデータがマスクされていないときはこの値は存在しない。

Payload
送信するデータそのもの。上の例ではマスクされているため、"test"ではなく"b84fdcba"となっている。これは元々のデータ"test"とマスキングキーの"cc2aafce"とのXORで求められた値。
今回はたまたまどちらも4バイトでわかりやすいが、送信データ4バイトでないことの多いはず。マスキングデータは次のようにして求められる。

マスク前データの i 番目のオクテット("original-octet-i")
  と 
マスキングキーの「iを4で割った余りの数」番目のオクテット("masking-key-octet-j")
  のXORが
変換後のデータの i 番目のオクテット("transformed-octet-i")

式で書くとこんな感じ。

j = i MOD 4
transformed-octet-i = original-octet-i XOR masking-key-octet-j

どっちもわかりにくいけど。。。

Unmask Payload
一つ上のPayloadがマスクされていてわかりにくいから、Wiresharkが気を利かせてマスクを解除したデータを表示してくれている。実際のWebSocketのフレームの中にはUnmask Payloadなんてものはないので注意。


次に、サーバが受信した"test"というテキストデータをそのままブラウザに送信したときのパケットキャプチャ。

81 04 74 65 73 74

Wiresharkで見たのがこれ。

意味は上で書いたとおり。サーバから送信するデータはマスクされていないことがわかる。