sgcIndy 2026.6 は、サーバー側に焦点を当てています。このリリースは、TLS、TCP、HTTP の各サーバーコンポーネントに一連のオプトイン式セキュリティ強化オプションを追加し、基盤となる Indy コードがこれまで防御してこなかった、よく知られた攻撃手法をいくつもふさぎます。具体的には、フェイルオープンな証明書検証コールバック、低速なドリップ型の Slowloris 接続、上限のないリクエストボディとヘッダー、そして HTTP リクエストスマグリングです。さらに、初の ML-KEM-768 ポスト量子鍵カプセル化プリミティブも提供します。
新しい保護機能はすべて、デフォルトでは従来の動作になります。そのため、有効化するまでは既存のアプリケーションに影響はありません。本記事では、すぐに貼り付けられる Delphi のスニペットとともに、それぞれを順に解説します。
強化された TLS サーバー
最も重要な修正は、OpenSSL のピア検証コールバックにあります。標準の Indy では、検証コールバックがフェイルオープンになり得ます。サーバーがクライアント証明書を要求すると、最終的な判断はユーザーコールバックに委ねられ、OpenSSL 自身の判定が無視されていました。そのため、成功を返すコールバックは、期限切れ、自己署名、または信頼されていない証明書を受け入れてしまいました。新しい TIdSSLOptions.StrictVerify フラグは OpenSSL の結果を強制するため、カスタムの OnVerifyPeer ハンドラは判断をさらに制限することしかできず、決して緩めることはできません。
さらに 3 つのフラグが、ヘッダーではすでに宣言されていたものの、ライブラリが一度も適用していなかった OpenSSL のオプションを有効にします。DisableCompression は CRIME を緩和し、DisableRenegotiation は OpenSSL 1.1.0h 以降でクライアント起点の再ネゴシエーション(CPU 非対称な DoS)をブロックし、ServerCipherPreference はクライアントではなくサーバー側の暗号スイート順序がネゴシエーションで優先されるようにします。
uses
IdHTTPServer, IdSSLOpenSSL;
var
oServer: TIdHTTPServer;
oSSL: TIdServerIOHandlerSSLOpenSSL;
begin
oServer := TIdHTTPServer.Create(nil);
oSSL := TIdServerIOHandlerSSLOpenSSL.Create(oServer);
oSSL.SSLOptions.CertFile := 'server.pem';
oSSL.SSLOptions.KeyFile := 'server.key';
oSSL.SSLOptions.SSLVersions := [sslvTLSv1_2, sslvTLSv1_3];
// opt-in hardening, every flag defaults to False
oSSL.SSLOptions.StrictVerify := True; // enforce the OpenSSL verdict
oSSL.SSLOptions.DisableCompression := True; // CRIME
oSSL.SSLOptions.DisableRenegotiation := True; // renegotiation DoS
oSSL.SSLOptions.ServerCipherPreference := True; // server picks the cipher
oServer.IOHandler := oSSL;
oServer.Active := True;
end;
不明なクライアント証明書を実際に拒否する相互 TLS が必要な場合は、StrictVerify とともに VerifyMode を [sslvrfPeer, sslvrfFailIfNoPeerCert] に設定してください。
Slowloris を阻止する
Slowloris クライアントは多数の接続を開き、1 バイトずつ少しずつ送信してリクエストを決して完了させず、すべてのワーカースレッドを占有し続けます。自然な防御策は読み取りタイムアウトのように見えますが、知っておくべき微妙な点があります。Indy の ReadTimeout は非アクティブタイムアウトです。バイトが到着するたびにリセットされるため、数秒ごとに 1 バイトを送るクライアントは接続を永遠に維持できてしまいます。
2026.6 では、SetReadDeadline を介して TIdIOHandler に本物の合計読み取りデッドラインを追加しました。これは読み取りごとにチェックされるため、バイトが少しずつ流れ込み続けている間でも発動します。HTTP サーバーは新しい RequestReadTimeout プロパティを通じてこれを自動的に有効にし、リクエストラインとヘッダーの受信にかかる時間を制限し、keep-alive リクエストごとにリセットします。
uses
IdHTTPServer;
var
oServer: TIdHTTPServer;
begin
oServer := TIdHTTPServer.Create(nil);
oServer.DefaultPort := 8080;
// close any client that has not finished sending the request
// line and headers within 5 seconds, even if it keeps dripping bytes
oServer.RequestReadTimeout := 5000;
oServer.Active := True;
end;
カスタムの TIdTCPServer では、読み取りループの周りに同じデッドラインを手動で適用できます。クリアするには 0 を渡してください。
procedure TForm1.ServerExecute(AContext: TIdContext);
var
vLine: string;
begin
// bound the whole request to 5 seconds of total read time
AContext.Connection.IOHandler.SetReadDeadline(5000);
try
vLine := AContext.Connection.IOHandler.ReadLn;
// ... handle the request ...
finally
AContext.Connection.IOHandler.SetReadDeadline(0);
end;
end;
HTTP サーバー向けのリクエスト制限
クライアントが宣言した内容をそのまま読み取る HTTP サーバーは、メモリ枯渇攻撃の格好の標的です。単一のリクエストが Content-Length: 2000000000 を宣言すれば、サーバーは 2 ギガバイトをバッファリングしようとし、あるいは終わりのないチャンク化ボディをストリーミングしたり、数百万バイトのヘッダーを送信したりします。2026.6 では、TIdCustomHTTPServer に 3 つの上限とスマグリングチェックを追加しました。
MaxRequestBodySize は Content-Length と合算されたチャンク化ボディに上限を設け、超過した場合は 413 Payload Too Large を返します。MaxHeaderTotalSize はヘッダーの合計バイト数に上限を設け、431 Request Header Fields Too Large を返します。StrictRequestParsing は、意図的にあいまいなリクエストを拒否します。たとえば Content-Length と Transfer-Encoding: chunked の両方を持つメッセージ(典型的なリクエストスマグリングのベクトル)や、負のチャンクサイズを持つものがそれに該当し、400 Bad Request を返します。チャンク化トレーラーヘッダーのループにも上限が設けられたため、攻撃者が無限のトレーラー行のストリームで接続を開いたままにすることはできなくなりました。
oServer.MaxRequestBodySize := 10 * 1024 * 1024; // 10 MB, else 413
oServer.MaxHeaderTotalSize := 64 * 1024; // 64 KB, else 431
oServer.StrictRequestParsing := True; // reject CL+TE smuggling, else 400
生の TCP レイヤーでは、TIdIOHandler.MaxInputBufferSize が IOHandler 上に構築されたあらゆるプロトコルの累積入力バッファに上限を設け、長さプレフィックス付きの読み取りや過大な行がバッファを際限なく増大させるのを防ぎます。
// inside OnConnect / OnExecute of any Indy server
AContext.Connection.IOHandler.MaxInputBufferSize := 1024 * 1024; // 1 MB cap
設計上オプトイン
これらのオプションはいずれもデフォルトの動作を変更しません。フィールドはすべてオフがデフォルトです(サイズとタイムアウトの制限は 0、ブール型フラグは False)。そのため、2026.6 にアップグレードしたプロジェクトは 2026.5 のときとまったく同じ動作になります。デプロイに必要な保護だけを正確に有効にでき、同じコードが Delphi 7 から RAD Studio 13、そして Free Pascal までコンパイルできます。
このリリースのその他の変更
2026.6 は、OpenSSL 3.5 以降で利用できる、初の ML-KEM-768 ポスト量子カプセル化およびデカプセル化プリミティブを導入します。これらはシンプルな TBytes API を公開しているため、従来の ECDH 交換と並行して、ハイブリッドハンドシェイクにポスト量子鍵カプセル化のステップを組み込めます。
ビルド面では、RAD Studio がスペースを含むパスにインストールされている場合に、C++Builder パッケージのコンパイルが MSBuild エラー MSB1008 で失敗することがなくなりました。DCC_BpiOutput パラメータが引用符で囲まれるようになりました。
アップグレード
2026.6 はドロップイン式のアップグレードです。破壊的変更はなく、移行すべきものもありません。新しい保護機能はすべてオプトインだからです。上記のスニペットを確認し、お使いのサーバーに合ったオプションを有効にしてください。
sgcIndy は無料です。最新ビルドは esegece.com/products/sgcindy/download からダウンロードできます。
ご質問、ご意見、またはサーバー強化のお手伝いが必要ですか? お問い合わせください。コードを書いた本人から返信があります。
