web-dev-qa-db-ja.com

HTTPS経由のウェブサーバーIPへの直接アクセスをブロックする

this および this question と同様に、ユーザーがIPを使用してサーバーにアクセスするのをブロックします。

HTTP(ポート80)の場合、これは正常に機能しますが、HTTPSの場合は機能しません。したがって、ユーザーはhttps://<myip> Webサーバーにアクセスし、nginxはデフォルトの証明書を返します。ところで、「通常の」サーバーブロック(ドメイン名を含む)でHTTP2を使用しているため、次のように使用します。

listen 443 ssl http2;
listen [::]:443 ssl http2;

これを試して、HTTPS IPアクセスをブロックしました:

server {
   listen 443 ssl;
   listen [::]:443 ssl;

   server_name _;
   return 444;
}

ただし、残念ながら、これはドメインが使用されているかどうかに関係なく、すべてのHTTPS要求をブロックします。もちろん、SNI以外のクライアントをブロックする必要があるかもしれません。もちろん、これらは使用済みのドメイン名をサーバーに配信しないので、問題はありません。 (とにかく、SNIをサポートしていないクライアントは古いです...)

私は通常、これをnginxでブロックすることを好みますが、ファイアウォールレベル(iptables)でこれをブロックすることについていくつかのアイデアがあれば、これらも喜んで感謝します。また、iptablesでブロックする方が良い理由を主張したい場合は、これを実行して、IPへのHTTP [またはその他すべての]リクエストもブロックするように説得できます。通常、接続のドロップは問題ありません(nginxステータスコード444と同様)。

ただし、要件が1つあります。サーバーのIPアドレスは動的IPであり、サーバーは動的DNSサービスを使用するため、サーバーのIPアドレスを構成で明示的に述べたくありません。

要するに、これが私が達成したいことです:

  • iP経由のアクセスをブロックする
  • ドメイン名によるアクセスを許可
  • 非SNIクライアントをブロックしても問題ありません
  • これにはファイアウォールブロッキングを使用しても問題ありません
  • 接続を削除しても問題ありません
  • サーバーのIPアドレスは言及しない
  • hTTPS経由でドメイン名を公開しない

編集:もう一度失敗しました。私は この提案 に従い、この構成スニペットを使用しようとしましたが、これは論理的には良いことのように思われます。

if ($Host != "example.com") {
        return 444;
}

基本的には機能しますが、「https://」にアクセスすると、nginxは最初にHTTPS証明書(ドメイン名を含む)をすでに送信していることがわかり、接続警告をスキップした場合にのみ、アクセスがブロックされていることがわかります。 nginxはHTTPS接続がある場合にのみHostヘッダーを読み取ることができるため、これは論理的ですが、この時点でnginxはすでにドメイン名を含むサーバー証明書を送信しているため、ユーザーはドメイン名を取得し、それを使用して再接続してIP全体を作成できます役に立たないブロッキング。また、nginxがすべてのリクエストのホストヘッダーをチェックするため、このソリューションのパフォーマンスの側面についても少し心配しています。そのため、ここでソリューションを探しています。

8
rugk

今日、私はこのブロックで同じ問題に遭遇しました:

server {
  listen 443 ssl default_server;
  server_name <SERVER-IP>;
  return 444;
}

Nginx-logsは言う:

"ssl_certificate"は、SSLハンドシェイク中にSSLポートでリッスンするサーバーで定義されていません

あなたが述べたように、これは他のサーバーブロックも無効にするようです。 server-ip-addressに(自己署名された)証明書を使用することで、これを修正できます。私はopensslを使用してこれを行いました:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout privateKey.key -out certificate.crt -subj '/CN=<SERVER-IP>'

次に、server-blockを次のように変更します。

server {
    listen 443 ssl default_server;
    server_name <server-ip>;
    ssl_certificate         /path/to/certificate.crt;
    ssl_certificate_key     /path/to/privateKey.key;
    return 444;
}

誰かがhttps経由でSERVER-IPを使用してサーバーにアクセスする場合、nginxは非表示にするドメイン名証明書ではなく、(自己署名)証明書を提示します。

ちなみに、server-block-with-IPをdefault_serverにします。このように、SNIが無効になっているクライアントは、ドメイン名証明書ではなく、IP証明書を取得します。これはopensslでもテストできます(SNIを有効にする-servernameオプションは省略されます)。

openssl s_client -connect <SERVER-IP>:443
8
BvE

これを試してください-default_serverビットは重要な部分です。それが機能しない場合は、443/SSLでリッスンしている他のサーバーの構成を示す質問を更新してください。

server {
  listen 443 ssl default_server;
  listen [::]:443 ssl default_server; # not sure if you want/need it here as well. Try both ways.

 server_name _;
 return 444;
}
2
Tim