【テクニカル・上級編】 Webソケット(WebSocket)のセキュリティとクロスサイト攻撃 – オフェンシブセキュリティ & リバースエンジニアリング防御ガイド

WebSocketの暗部:Origin検証の虚構と「見えない」セッションジャック

多くのアーキテクトが誤解していることがある。WebSocketは「ただのTCPの延長線上にあるHTTPアップグレード」ではない。それは、一度確立されればレイヤ7の制約をかなぐり捨て、恒久的なデータパイプラインとしてサーバーの心臓部へ直結する「裏口」だ。

HTTPのステートレスな世界から、WebSocketのステートフルな世界へ移行する際、多くのエンジニアが「認証はハンドシェイク時に済ませればいい」と判断する。だが、これが脆弱性の温床になる。今日は、その盲点を突き、どう防衛すべきかという深淵の話をしよう。

—

1. Origin検証という「ザル」の正体

WebSocketのハンドシェイクにおいて、ブラウザは Origin ヘッダーを送信する。多くのWebサーバーやライブラリは、この値を検証することでクロスサイト・WebSocket・ハイジャック(CSWSH)を防ごうとする。

しかし、攻撃者は Origin ヘッダーをいじれるわけではない。彼らが狙うのは「検証ロジックの不備」だ。

攻撃者の視点:ホワイトリストの甘い罠

よくある脆弱な実装例を見てみよう。

// 脆弱なOrigin検証の例(Node.js/wsライブラリ想定)
const wss = new WebSocket.Server({
  verifyClient: (info) => {
    // 悪い例:indexOfで前方一致をチェックしている
    // これでは "https://trusted-app.com.attacker.com" を許容してしまう
    return info.origin.indexOf('https://trusted-app.com') === 0;
  }
});

この実装では、攻撃者が trusted-app.com.attacker.com というドメインを取得し、そこから攻撃用のJavaScriptをロードすれば、いとも簡単に Origin チェックをバイパスできる。

防衛の鉄則:
Origin 検証は、必ず「完全一致」で行うこと。また、サブドメインの動的生成が許容される設計なら、正規表現による厳密なバリデーションか、あるいはそもそも Origin ヘッダーを信頼しないアーキテクチャ(後述のトークンベース認証)を採用すべきだ。

—

2. ハンドシェイク後の「認証の剥落」

WebSocket通信において、最も深刻なミスは「ハンドシェイク時のCookie認証のみに頼ること」だ。

WebSocketは一度接続が確立されると、HTTPのCookie認証が継続して適用されるわけではない。ハンドシェイクが完了した瞬間に、その接続は「独立したパイプ」となる。もしアプリケーションがメッセージのたびに認証トークンを再検証していない場合、CSRFやCSWSHによって確立されたセッションが、そのまま攻撃者の意のままに操作されることになる。

アーキテクチャの解法:メッセージごとの署名

理想的な設計は、WebSocketの各フレーム(メッセージ)に認証トークン(JWT等)を含めることだ。

// クライアント側:メッセージ送信時のトークン付与
const socket = new WebSocket('wss://api.example.com');

socket.send(JSON.stringify({
  type: 'TRADE_EXECUTION',
  token: 'eyJhbGciOiJIUzI1NiIsInR5...', // 毎回、署名付きトークンを検証させる
  payload: { amount: 1000, asset: 'BTC' }
}));

サーバー側では、この token をメッセージを受け取るたびに解読・検証する。これにより、たとえセッションがハイジャックされても、攻撃者が有効なトークンを生成できない限り、コマンドは拒絶される。

—

3. 生成AI時代の新たな脅威:プロンプトインジェクションとWebSocket

近年、WebSocket経由でLLM(大規模言語モデル)のストリーミングレスポンスを受けるアーキテクチャが増えている。ここで注意すべきは、WebSocketメッセージが「AIのプロンプトに対するガードレイルをすり抜ける経路」になり得ることだ。

クライアントから送られてきたWebSocketメッセージを、そのままLLMのコンテキストに注入していないだろうか?もしそうなら、それはSQLインジェクションの現代版、すなわち「プロンプトインジェクション」を広大な攻撃面に晒しているに等しい。

対策のアーキテクチャ:
1. 入力の正規化: WebSocket経由のメッセージを、一旦「JSONスキーマ検証」に通す。
2. 命令分離: ユーザー入力とシステムプロンプトを、データ構造レベルで厳格に分離する(role: "user" のようなメタデータ管理)。
3. 出力のサニタイズ: LLMからの応答をブラウザへ転送する前に、HTMLタグや実行可能なコードが含まれていないか、あるいはDOM汚染のリスクがないかを確認する。

—

4. 結び:耐量子暗号と次世代の監査観点

最後に、少し未来の話をしよう。現在、WebSocketはTLS 1.3によって暗号化されているが、将来的な「量子コンピュータによる解読(Shorのアルゴリズム等)」を考慮すれば、現在のRSAやECDSAを用いた鍵交換は脆弱と言わざるを得ない。

高セキュリティな金融や国家機密を扱うインフラでは、すでに耐量子暗号(PQC: Post-Quantum Cryptography)への移行が始まっている。WebSocketの実装においても、TLS 1.3の鍵交換にKyberのようなアルゴリズムを採用したプロキシ層を前段に置くなど、物理層に近いレイヤでの防衛強化が今後必須となる。

チーフホワイトハッカーとしての提言:
「WebSocketは便利だ。しかし、それは脆弱なパイプラインである」という前提を忘れてはならない。認証はハンドシェイクで終えるな。メッセージのバリデーションは、スキーマ定義だけでなく「意味論的な解析(Semantic Analysis)」まで踏み込め。

脆弱性は常に、設計の「境界線」に潜んでいる。あなたのシステムの境界線がどこにあるのか、今一度再確認することをお勧めする。

コメント

タイトルとURLをコピーしました