要約

  • WebSocketのマスク鍵は4バイトともフレーム内で公開される。秘密を守るのではなく、アプリケーションが送信前にワイヤ上の姿を指定できないようにする仕組みである。
  • 背景には中間装置の実害があった。クライアントフレームは毎回新しい鍵でマスクし、サーバーフレームはマスクせず、送信開始後のペイロード変更も許さない。

初期のフレームは境界を信じていた

IETFの2010年5月版ドラフトは、HTTPで始めた接続を双方向のブラウザー通信へ移す、小さなワイヤ形式を示した。テキストフレームは0x00で始まり0xffで終わり、別形式では長さを運んだ。WebSocketを理解する両端にとっては、それでメッセージの境目が分かった。

しかし、経路に以前から置かれていた機器まで同じ状態遷移を理解するとは限らない。インターセプト型プロキシがUpgradeを通した後も、続くストリームから新たなHTTP要求を探し続けることがあった。端点の文法は切り替わったのに、中間のパーサーだけが元の世界に残ったのである。

ここで危険なのは、悪意あるプロキシとは限らない。ブラウザーとサーバーが合意を守っていても、第三者が次のバイト列に誤ったプロトコル名を与えれば、どちらも意図しなかったキャッシュ操作が起き得る。

一定の変換では送信者の支配が残った

2011年1月の-04ドラフトは、クライアントからサーバーへの全フレームをマスクするよう求めていた。ただし鍵はハンドシェイク値から得られ、接続中ずっと同じだった。

一定で既知の変換は見た目を変えても、結果を選ぶ能力までは消さない。規則を予測できるアプリケーションは、欲しい出力から逆算して入力を決められる。必要だったのは装飾的な難読化ではなく、フレームごとに新しい決定点を作ることだった。

アプリケーションは論理的な内容を選べる。変換も後から知れる。しかし送信が確定する前に、その二つを同時に握ってワイヤ上の列を設計してはならない。この順序こそ防御の中心だった。

プロキシが応答に別の名前を付けた

2011年の論文Talking to Yourself for Fun and Profitは、透明プロキシ、より正確にはインターセプト型プロキシを通るブラウザーソケットを調べた。一部の装置は同意やUpgradeを転送しながら遷移を理解せず、その後の攻撃者制御データをHTTP要求として読んだ。

攻撃の因果は複数の主体をまたぐ。悪意あるサイトがブラウザーを攻撃者のサーバーへ接続させる。混乱した中間装置が、後続バイト列を別の資源に対する要求だと解釈する。攻撃サーバーが応答らしいデータを返す。キャッシュがそれを偽の資源名で保存し、別の利用者に本来のオリジン由来でない内容を渡す。

研究者の2011年3月の広告実験では、測定したJavaとFlashの経路の少数ながらゼロではない範囲でキャッシュ汚染条件が見つかった。Upgradeを使うWebSocket試案では、テストまで到達した47,338件のハンドシェイク中8件が成功した。この値は当時の実験についての値であり、現在のプロキシ普及状況を示さない。

論文が残した核心は比率ではない。HTTPらしくない接頭辞を足しても、未知の壊れたパーサーがそれを読み飛ばさないとは証明できない。危険なバイト列を、敵対的なアプリケーションが意図通りに構築できないようにする方が強い。

鍵はフレームごとに置かれた

2011年2月の-05ドラフトで決定的な変更が入った。各クライアントフレームが独自の32ビット鍵を持ち、強いエントロピー源から選び、過去の鍵から次を予測できないようにした。RFC 4086が注意する通り、時計やカウンター、小さなシードからの出力は、見た目が不規則でも敵には読める場合がある。

最終版のRFC 6455も逐次鍵を採った。MASKビットが4鍵オクテットの存在を示し、ペイロードのオクテットiを鍵のi mod 4番目とXORする。変換で長さは変わらず、鍵はペイロード長に含まれない。

受信者は鍵を使って元に戻す。観測者にも同じことができる。それでよい。アプリケーションが内容を引き渡した後、予測できない鍵がワイヤ表現を決めるという順番に意味がある。機密性を主張する仕組みではない。

送信を始めたフレームは不変になった

新しい鍵だけでも穴は残る。長いフレームの既知の冒頭から繰り返しXORを推定し、まだ送っていない末尾をアプリケーションが書き換えられるなら、その末尾をマスク後にHTTP要求らしくできる。

そこでRFC 6455は時間的な境界を設ける。クライアントフレームの送信が始まったら、そのフレームのアプリケーション由来ペイロードをアプリケーションが変更してはならない。追加・変更データは別フレームに入れ、別の新しい鍵を使う。

ランダム性だけでなく、取り消せないコミットが必要なのである。開始前はアプリケーションが内容を所有し、開始後はクライアント実装が確定した列を預かる。この引き渡しがなければ、先頭の観測が末尾の制御に変わってしまう。

方向が脅威モデルを語った

クライアントからサーバーへのフレームはすべてマスクする。サーバーからクライアントへのフレームはマスクしてはならない。方向を誤ったフレームを受け取った端点は接続を閉じ、プロトコルエラー1002を使うこともできる。

非対称なのは、攻撃がブラウザーからサーバーへ流れる要求らしい列を必要としたからだ。悪意あるサーバーはもともと応答らしいデータを選べる。しかし偽の要求がなければ、混乱したキャッシュはその応答を誤った資源名へ結び付ける半分を欠く。

もちろん、サーバー方向のデータが安全だという意味ではない。認証、認可、Origin方針、内容検証、通信路保護には別の責任がある。この特定の攻撃に、逆方向のマスクは効く制御ではなかったというだけだ。

TLSがあっても規則は消えなかった

クライアントマスクはwsにもTLS内のwssにも適用される。正しく暗号化されたストリームを経路装置は読めないため、後者では冗長に見える。しかしTLSは端点間の機密性と完全性を扱い、マスクは信頼できないブラウザーコードが適合クライアントにどのフレーム列を出させられるかを制限する。契約が異なる。

TLS終端がゲートウェイに移り、その先に内部ホップが残る場合もある。確立方法や配置の変更によってフレーム妥当性が変わらない方がよい。一層の保護が、別層の文法を暗黙に免除してはならない。

HTTP/2とHTTP/3にも義務が引き継がれた

RFC 8441は、HTTP/2ストリーム上のExtended CONNECTでWebSocketを開始できるようにした。:protocolが遷移を示すため、HTTP/1.1のSec-WebSocket-KeySec-WebSocket-Acceptの処理はこの経路から外れた。それでもフレームマスクは廃止されず、ハンドシェイクのSHA-1を扱う10.8節を除いてRFC 6455第10節の考慮事項が適用される。

RFC 9220はExtended CONNECTをHTTP/3へ移し、新たなセキュリティ例外を設けなかった。外側はHTTP/1.1接続全体の切り替えからHTTP/2の一つのストリーム、さらにQUICストリームへ変わったが、WebSocketフレームの方向証拠は残った。

つまりマスクは、Upgradeヘッダー一つの応急処置ではなかった。アプリケーションの選択、クライアントのコミット、中間装置の誤読という関係を符号化したため、周囲の転送方式が変わっても意味を保った。

公開鍵が示すこと、示さないこと

新しいマスク鍵は、送信者の身元、Originの許可、操作の認可、TLS外での完全性を証明しない。キャプチャに鍵が見えるのは正常であり、鍵の再利用や予測可能性こそ安全仮定の破れを示す。

適合マスクがすべての中間装置を修理するわけでもない。RFC 6455は、不適合なクライアントやサーバーなら脆弱なプロキシを同種の攻撃にさらし得ると述べる。プロトコルが狭めたのは、適合ブラウザー経路に何を故意にさせられるかであって、世界中のキャッシュを支配したのではない。

歴史が伝えるのは控えめな原則である。古い基盤が共有経路のバイト列を誤った文法で読む恐れがあるとき、新プロトコルは自らの正しさを宣言するだけでは足りない。不信な参加者が危険な列を意図的に置く力まで制限する必要がある。

情報源と証拠の限界

ドラフトとRFCは設計の変遷と規範動作を裏付ける。論文は境界のある2011年の実験であり、現在の脆弱性や市場比率ではない。特定の現行ブラウザー、プロキシ、CDN、ゲートウェイの挙動は、これらの資料だけでは判断できない。マスクは暗号、完全性保護、端点認証でも、キャッシュ応答のオリジン帰属証明でもない。