sgcWebSockets の HTTP サーバーを堅牢化する

· コンポーネント

WebSocket サーバーは、ほとんどの場合 HTTP も話します。ソケットを開くページを配信し、ヘルスチェックに応答し、API をホストし、あるいはフロントエンドへリダイレクトします。その HTTP の接触面には独自のよく知られた攻撃クラスがあり、最新の sgcWebSockets はその中でも最も重要なものをデフォルトでふさぎます。これは両方の HTTP サーバー、すなわち Indy ベースの TsgcWebSocketHTTPServer と Windows の http.sys ベースの TsgcWebSocketServer_HTTPAPI の両方に適用されます。

パストラバーサルを封じる

DocumentRoot を設定して静的ファイルを配信する場合、サーバーは以前、ドキュメントルートとリクエストパスを連結してファイルパスを組み立てていました。GET /../../../../windows/win.ini のようなリクエスト、あるいはその URL エンコード形式である /..%2f..%2f... は、Web ルートの外に出て任意のファイルを読み取ることができました。サーバーは現在、解決されたパスを正規化し、それが DocumentRoot の内側にとどまる場合のみ配信します。外に出るものはすべて拒否されます。これは HTTP/1.x と HTTP/2 の両方の経路で常に有効であり、設定は不要です。

リクエストボディに上限を設ける

HTTP リクエストボディには組み込みのサイズ制限がないため、クライアントは巨大な Content-Length を宣言し、ギガバイト単位のデータをストリーミングしてサーバーのメモリを枯渇させることができました。新しい MaxRequestBodySize プロパティはこれに上限を設けます。デフォルトは 64 MB で、それを超える量を宣言したリクエストは、ボディがバッファリングされる前に 413 で拒否されます。大きなアップロードのために値を引き上げたり、0 に設定して制限を無効にしたりできます。

oServer := TsgcWebSocketHTTPServer.Create(nil);
oServer.Port := 80;
oServer.MaxRequestBodySize := 16 * 1024 * 1024;  // 16 MB, reject larger with 413
oServer.Active := True;

より厳格なリクエスト解析でスマグリングを止める

HTTP リクエストスマグリングは、あるリクエストの終わりと次のリクエストの始まりの位置について見解が一致しないサーバーを悪用するもので、典型的には Content-LengthTransfer-Encoding: chunked の両方のヘッダーを送信します。新しい StrictRequestParsing プロパティはデフォルトで有効になっており、このような曖昧なリクエストを 400 で拒否し、より厳格な chunked エンコーディングの検証を適用します。これにより、フロントエンドのプロキシとサーバーが非同期化されることはありません。

oServer := TsgcWebSocketHTTPServer.Create(nil);
oServer.StrictRequestParsing := True;   // default: reject Content-Length + Transfer-Encoding
oServer.MaxRequestBodySize := 64 * 1024 * 1024;
oServer.Active := True;

HTTP/2 Rapid Reset 防御

HTTP/2 Rapid Reset 攻撃 (CVE-2023-44487) は、ストリームを開いてすぐに RST_STREAM でキャンセルすることをタイトなループで繰り返し、クライアントにはほとんどコストをかけずにサーバーへ際限のないリクエスト処理を強いるものです。HTTP/2 サーバーは現在、単一の接続が送信できるリセットおよび制御フレームの数に上限を設け、同時ストリーム数 100 という有限のデフォルトを備え、不正な接続を GOAWAY フレームで閉じます。HTTP/2 はオプトインであるため、これが意味を持つのは有効にした場合のみですが、有効にしていれば防御は自動的に働きます。

よりクリーンなレスポンスヘッダー

アプリケーションが、リクエストに影響された値、たとえばリダイレクト先やエンティティタグからレスポンスヘッダーを組み立てる場合、その値に紛れ込んだキャリッジリターンやラインフィードがレスポンスを分割し、余分なヘッダーを注入することができました。http.sys サーバーは現在、LocationServerETag などのレスポンスヘッダー値から CRLF を取り除くため、単一の値が二つのヘッダーになることはもはやありません。

ファイアウォールの位置づけ

これらの保護は、サーバー自身の HTTP パーサーとファイルサーバーの中に位置しており、これは FirewallRateLimiter コンポーネントが到達できないレイヤーです。IP フィルタリング、レート制限、オリジンポリシーにはファイアウォールを使い続け、解析と静的配信は組み込みの防御に守らせてください。公開サーバーでは、実際にファイルを配信するときにのみ DocumentRoot を設定し、MaxRequestBodySize を実際の最大値に合わせて調整し、MaxConnections に上限を設けることもお勧めします。

アップグレード

新しい保護は、更新するとすぐに安全なデフォルト設定で有効になるため、ほとんどのサーバーはコードを変更することなく堅牢化を得られます。アプリケーションが 64 MB を超えるリクエストボディを受け付ける場合は、それに応じて MaxRequestBodySize を引き上げてください。既存のクライアントに影響はありません。

sgcWebSockets のダウンロードページから更新するか、GetIt または登録済みアカウントを通じて入手してください。

ご質問、ご意見、または移行のお手伝いが必要ですか?お問い合わせください。コードを書いた本人から返信が届きます。