4. Opening Handshake (オープニングハンドシェイク)
WebSocketプロトコルのオープニングハンドシェイク (Opening Handshake) は、既存のHTTPインフラストラクチャと互換性を持つように設計されています。このハンドシェイクはHTTPアップグレードリクエストです。
4.1 Client Requirements (クライアント要件)
クライアントが送信するハンドシェイクリクエストは、以下の形式の有効なHTTPリクエストでなければなりません:
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Extensions: permessage-deflate
必須ヘッダーフィールド
Host
- サーバーのホストとポート番号を含む
- HTTP/1.1仕様に準拠
Upgrade: websocket
- このヘッダーを含まなければならない (MUST)
- 値は"websocket"(大文字小文字を区別しない)
Connection: Upgrade
- このヘッダーを含まなければならない (MUST)
- これがアップグレードリクエストであることを示す
Sec-WebSocket-Key
- このヘッダーを含まなければならない (MUST)
- ランダムに生成された16バイトの値で、Base64エンコードされている
- サーバーがWebSocketプロトコルを理解していることを検証するために使用
Sec-WebSocket-Version: 13
- このヘッダーを含まなければならない (MUST)
- 現在のバージョン番号は13
オプションのヘッダーフィールド
Origin
- ブラウザクライアントは含まなければならない (MUST)
- 非ブラウザクライアントは含んでもよい (MAY)
- サーバー側のセキュリティ検証に使用
Sec-WebSocket-Protocol
- 任意 (OPTIONAL)
- クライアントがサポートするサブプロトコルのリストを指定
- 複数の値はカンマで区切る
Sec-WebSocket-Extensions
- 任意 (OPTIONAL)
- クライアントがサポートする拡張を指定
- 形式:
extension-name [; parameter=value]*
4.2 Server-Side Requirements (サーバー側要件)
サーバーはクライアントハンドシェイクを検証し、適切なレスポンスを返さなければなりません。
4.2.1 Reading the Client's Opening Handshake (クライアントハンドシェイクの読み取り)
サーバーは以下を検証しなければなりません (MUST):
- HTTPメソッド: GETでなければならない
- HTTPバージョン: 少なくともHTTP/1.1
- 必須ヘッダー: Host、Upgrade、Connection、Sec-WebSocket-Key、Sec-WebSocket-Versionが存在しなければならない
- Sec-WebSocket-Version: 13(またはサーバーがサポートするバージョン)でなければならない
検証が失敗した場合、サーバーは適切なHTTPエラーステータスコードを返すべきです (SHOULD)。
4.2.2 Sending the Server's Opening Handshake (サーバーハンドシェイクの送信)
検証が成功した場合、サーバーはハンドシェイクレスポンスを送信しなければなりません (MUST):
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Acceptの計算
これはハンドシェイクの重要な部分です。サーバーは以下の手順で計算しなければなりません:
- クライアントの
Sec-WebSocket-Key値を取得 - 固定のGUID文字列と連結:
258EAFA5-E914-47DA-95CA-C5AB0DC85B11 - 連結された文字列のSHA-1ハッシュを実行
- ハッシュ結果をBase64エンコード
アルゴリズム例 (JavaScript):
const crypto = require('crypto');
function generateAcceptKey(clientKey) {
const GUID = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
const concatenated = clientKey + GUID;
const hash = crypto.createHash('sha1').update(concatenated).digest();
return hash.toString('base64');
}
// 例
const clientKey = 'dGhlIHNhbXBsZSBub25jZQ==';
const acceptKey = generateAcceptKey(clientKey);
console.log(acceptKey); // s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
4.3 Collecting Extensions and Subprotocols (拡張とサブプロトコルの収集)
サブプロトコルのネゴシエーション
クライアントはSec-WebSocket-Protocolヘッダーに複数のサブプロトコルをリストできます:
Sec-WebSocket-Protocol: chat, superchat
サーバーはクライアントのリストから1つを選択(または選択しない)し、レスポンスで返さなければなりません (MUST):
Sec-WebSocket-Protocol: chat
サーバーがクライアントが要求したサブプロトコルのいずれもサポートしていない場合、Sec-WebSocket-Protocolヘッダーを省略できます。
拡張のネゴシエーション
拡張のネゴシエーションはSec-WebSocket-Extensionsヘッダーを通じて行われます:
クライアントリクエスト:
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
サーバーレスポンス:
Sec-WebSocket-Extensions: permessage-deflate
サーバーは拡張を受け入れ、パラメータを変更、または拒否できます。
4.4 Supporting Multiple Versions (複数バージョンのサポート)
サーバーがクライアントが要求したバージョンをサポートしていない場合、HTTP 426 Upgrade Requiredを返し、サポートされているバージョンをリストしたSec-WebSocket-Versionヘッダーを含めるべきです (SHOULD):
HTTP/1.1 426 Upgrade Required
Sec-WebSocket-Version: 13, 8, 7
ハンドシェイクの完全な例
成功したハンドシェイク
クライアント → サーバー:
GET /chat HTTP/1.1
Host: example.com:8000
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat
Sec-WebSocket-Version: 13
Origin: http://example.com
サーバー → クライアント:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
ハンドシェイク失敗の例
サポートされていないバージョン:
HTTP/1.1 426 Upgrade Required
Sec-WebSocket-Version: 13
Originが受け入れられない:
HTTP/1.1 403 Forbidden
セキュリティに関する考慮事項
- Originの検証: サーバーは
Originヘッダーを検証し、クロスサイト攻撃を防ぐべきです - TLSの使用: 本番環境ではwss://を使用
- すべてのヘッダーの検証: すべての必須ヘッダーが存在し、適切にフォーマットされていることを確認
- 接続の制限: DoS攻撃を防ぐためのレート制限を実装
参考リンク
- 前の章: 3. WebSocket URIs
- 次の章: 5. Data Framing (データフレーミング)
- 実装例: ハンドシェイクキー計算