nginx での SNI 非対応の HTTPS 通信の対処方法
はじめに
現代のウェブサーバー運用では1台のサーバーで複数のHTTPSサイトをホストするのが一般的になりました。 このようなマルチホスティング環境で問題になるのが、SNI非対応のHTTPSアクセスや意図しないIP直打ちアクセスです。
この記事では、このようなリクエストを安全に拒否するための方法として、nginx 1.19.4 以降で利用可能な ssl_reject_handshake ディレクティブを紹介します。
SNIとその仕組み
SNI (Server Name Indication) は、TLS/SSL ハンドシェイクの初期段階でクライアントが「どのホスト名にアクセスしたいか」をサーバーへ通知する仕組みです。これにより、1つのサーバーIP上で複数のHTTPSサイトをホストしている場合でも、サーバーは正しいSSL証明書を選択できます。
通常のHTTPS通信の流れを簡略化すると以下のようになります。
- TCP接続確立
- TLS/SSLハンドシェイク開始
- ClientHello(SNI拡張に example.com を含む)
- ServerHello(SNIに対応した証明書を提示)
- 証明書検証・暗号化確立
- HTTPリクエスト送信(Host: example.com ヘッダー)
- HTTPレスポンス受信
SNI非対応の古いクライアントでは、ステップ3でホスト名を通知できないため、サーバーは「デフォルトの証明書」を返すしかありません。その結果、意図しない証明書が提示され、誤動作やセキュリティリスクにつながる可能性があります。
なお、現代の主要なCDN(例:AWS CloudFrontの通常利用、CloudflareのProxiedモードなど)では、すでにSNI対応を前提とした仕組みが採用されています。そのため、一般的なウェブ利用環境ではクライアントがSNI非対応であること自体が大きな問題になることはほとんどありません。
しかし、サーバー側で「デフォルト証明書が不用意に露出する」リスクは残るため、nginxにおける適切なフォールバック設定は依然として重要です。
問題について
マルチドメインのホスティングを同一のサーバー上で行う場合、設定次第では次のような問題が発生します。
- IP直アクセス時の証明書露出:サーバーのIPアドレスに直接HTTPSでアクセスすると、意図しない証明書(別サービス用やテスト用)が提示されることがあります。
- 意図しないアクセスの成立:クライアントがSNIを送信しない場合、nginxは「デフォルトの証明書」を返してしまうため、本来は拒否されるべき接続が成立してしまう可能性があります。ユーザーが証明書警告を無視すれば、そのまま通信が続行できてしまうケースもあります。
これらは「古いSNI非対応クライアント」に限らず、単純な誤設定や不正アクセスの試行によっても起こり得ます。特に共有環境や複数サービスを同一サーバーで運用している場合、証明書の誤提示はセキュリティリスクにつながります。
そこでnginxでは、フォールバック用の default_server を定義し、想定外のHTTPSリクエストをTLSハンドシェイクの段階で拒否することが推奨されます。この際に役立つのが、ssl_reject_handshake ディレクティブです。
解決方法
nginx 1.19.4 以降では、ssl_reject_handshake ディレクティブを利用して、想定外のHTTPSリクエストをハンドシェイク段階で拒否できます。これにより、誤った証明書の提示や意図しない接続の成立を防止できます。
推奨されるフォールバック設定例は以下のとおりです。
server { listen 443 ssl default_server; server_name _; ssl_reject_handshake on; }
この設定では、IPv4の通信で server_name に一致しないすべてのHTTPSリクエストに対して、TLSハンドシェイクの時点で接続を拒否します。 従来は「ダミー証明書を置いて誤接続を握り潰す」方法しかありませんでしたが、ssl_reject_handshake を使えば証明書自体が不要となり、管理の手間を大きく削減できます。
おわりに
default_server と ssl_reject_handshake を組み合わせることで、SNI非対応のHTTPS接続や意図しないIP直アクセスを安全に拒否できます。これにより、以下のような利点が得られます。
- セキュリティの向上:不要な証明書の露出を防ぎ、意図しない接続を確実に遮断できます。
- 運用の簡素化:ダミー証明書を用意・更新する必要がなくなります。
- 効率的なリソース利用:TLSハンドシェイクの段階で接続を拒否できるため、サーバーリソースの浪費を防げます。
複数サービスを1台のサーバーで運用している場合や、リダイレクト専用サーバーのように「許可されたドメイン以外を必ず拒否したい」環境では、この設定を強く推奨します。