sgcWebSockets における WebSocket サーバーの堅牢化

· コンポーネント

公開インターネットに面した WebSocket サーバーは、格好の標的です。このプロトコルには組み込みのメッセージサイズ制限がないため、たった 1 つの接続が細工されたフレーム 1 つでプロセス全体を引きずり落とそうとすることができ、アップグレードのハンドシェイクは杜撰な検証を探る格好の場所になります。最新の sgcWebSockets は、これらの入り口を既定で閉じる一連の保護機能を搭載しており、敵対的なトラフィックの下でもサーバーが稼働し続けます。

以下のすべての内容は、3 つの WebSocket サーバーすべてに適用されます。TsgcWebSocketServerTsgcWebSocketHTTPServer、そして http.sys ベースの TsgcWebSocketServer_HTTPAPI です。.NET 版は同じネイティブライブラリ上で動作するため、同じ保護機能を継承します。

3 種類のメモリ攻撃を止める 1 つの制限

目玉となる追加機能は、受信メッセージの最大サイズを制限する単一のプロパティ MaxMessageSize です。既定値は 64 MB で、ほぼすべてのアプリケーションにとって十分な大きさですが、引き上げたり引き下げたりすることも、0 に設定して制限を無効化することもできます。

この 1 つのプロパティが、いずれもサーバーの RAM を枯渇させるという同じ結末を迎える、3 種類の異なるメモリ枯渇手法に対して防御します。

いずれの場合も、接続は WebSocket close code 1009 (Message Too Big) でクリーンにクローズされ、サーバーのメモリが上限を超えることはありません。

oServer := TsgcWebSocketServer.Create(nil);
oServer.Port := 80;
// accept messages up to 16 MB, reject anything larger with close 1009
oServer.MaxMessageSize := 16 * 1024 * 1024;
oServer.Active := True;

安全なフレーム長の解析

WebSocket フレームは 64-bit の長さフィールドを保持できます。RFC 6455 によれば、そのフィールドの最上位ビットはゼロでなければなりません。sgcWebSockets は、64-bit の長さの最上位ビットが立っているフレームを信頼するのではなく拒否するようになったため、上記のサイズ制限を、ラップアラウンドする整数ですり抜けることはできません。このチェックは常に有効で、MaxMessageSize には依存しません。

より厳格なハンドシェイク

SecurityOptions 配下の 2 つの新しいオプションが WebSocket アップグレード自体を堅牢化し、どちらも既定で有効です。

EnforceWebSocketVersion は、13 以外のバージョンを要求するハンドシェイクに対して、アップグレードを完了する代わりに 426 Upgrade RequiredSec-WebSocket-Version: 13 ヘッダーで応答します。ValidateWebSocketKey は、Sec-WebSocket-Key が欠落しているか、有効な 16 バイトの base64 ノンスでないハンドシェイクを 400 Bad Request で拒否します。どちらのチェックも RFC 6455 のパスにのみ適用されるため、レガシー仕様の古いクライアントには影響しません。

oServer := TsgcWebSocketServer.Create(nil);
oServer.SecurityOptions.EnforceWebSocketVersion := True;  // 426 on a wrong version
oServer.SecurityOptions.ValidateWebSocketKey := True;     // 400 on a malformed key
// lock the server to your own site while you are at it
oServer.SecurityOptions.OriginsAllowed := 'https://app.example.com';
oServer.Active := True;

ファイアウォールとの連携

これらの保護機能は、フレームパーサーの内部、つまりプロトコル層に存在します。そこはまさにメモリ攻撃が発生する場所です。それは Firewall および RateLimiter コンポーネントが届かない部分です。なぜなら、これらはメッセージがデコードされた後にしかメッセージを見ないからです。この 2 つの層は互いを補完します。IP フィルタリング、接続およびメッセージのレート制限、オリジンポリシーにはファイアウォールとレートリミッターを引き続き使用し、パーサー自体は新しい組み込み制限に守らせてください。公開サーバーには、MaxMessageSize を実際の最大値に設定し、OriginsAllowed をフロントエンドにロックし、MaxConnections に上限を設けることをお勧めします。

アップグレード

新しい保護機能は、更新するとすぐに安全な既定値で有効になるため、ほとんどのサーバーは一切のコード変更なしにメモリおよびハンドシェイクの堅牢化を得られます。アプリケーションが正当に 64 MB を超えるメッセージをやり取りする場合は、MaxMessageSize をそれに応じて引き上げてください。既存のクライアントコードには影響しません。

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

ご質問、フィードバック、または移行のサポートが必要ですか? お問い合わせください。コードを書いた本人から返信が届きます。