故事是這樣的。我用 HTTPS Nginx Docker 懶人安裝法架了一台私人網站,雖然有 DNS 名稱但從未對外公開。Nginx 設定有限定 server_name private-web.nobody.know.com;,依我的理解,必須知道 DNS 名稱輸入 https://private-web.nobody.know.com,Nginx 才接受請求,允許連上網站。

前幾天,無意間發現這台完全沒對外公開的網站,怎麼還是引來網路上的惡意機器人瘋狂嘗試各種已知 URL 漏洞,還有幾次歪打正著成功連上網站:

大驚! 莫非攻擊者有能力查出 DNS 名稱?還是我犯了低級錯導致 DNS 名稱外流?後來在 Log 看到 Referrer 欄位出現 http://xxx.xxx.xxx.xxx(主機對外 IP),這才發現我的 Nginx 設定允許在 HTTPS URL 用 IP 當主機名稱,並會導向我的預設 HTTPS 網站,而瀏覽器還會顯示連線不安全,因為憑證簽發的對象是 private-web.nobody.know.com 而非 IP,而有心人只要查看憑證資訊,網站的正式 DNS 名稱便曝光了! (想到這,不禁漏了兩滴...)

經過研究,發現是我的 Nginx 沒設定好造成的。

我的 Ngnix 設定做法是在 /etc/nginx/nginx.conf 宣告 include /etc/nginx/conf.d/*.conf;做法,真正的設定內容則存在 /etc/nginx/conf.d 目錄的多個檔案。

conf.d/00.default.conf,處理 HTTP 80,接受供 Let's Encrypt TLS 憑證驗證請求,其餘改導向 HTTPS:

server {
    # Listen on plain old HTTP
    listen 80 default_server;

    # Pass this particular URL off to certbot, to authenticate HTTPS certificates
    location '/.well-known/acme-challenge' {
        default_type "text/plain";
        proxy_pass http://localhost:1337;
    }

    # Everything else gets shunted over to HTTPS
    location / {
        return 301 https://$http_host$request_uri;
    }
}

conf.d/01.aspnetcore.conf,處理 HTTPS 443,設定 TLS 憑證,用 Reverse Proxy 導向 ASP.NET Core Docker:

server {
    listen              443 ssl http2;
    server_name         private-web.nobody.know.com;
    ssl_certificate     /etc/letsencrypt/live/private-web.nobody.know.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/private-web.nobody.know.com/privkey.pem;

    location / {
        proxy_pass         http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection keep-alive;
        proxy_set_header   Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
    }
}

針對 443 ssl http2 我指定了 server_name 必須是 FQDN,但忽略了 Nginx 的一個行為,當有 listen 443 但沒有匹配的 server_name,Nginx 會套用預設 server 區塊 的設定:參考

Nginx 的 server_name 比對套用規則:

  1. 首先嘗試找到 server_name 與請求的 Host 標頭完全匹配的 server 區
  2. 尋找 server_name 前置萬用字元(*)相符合的 server 區塊
  3. 若沒前置 * server_name 匹配,接著尋找 server_name 後置 * 字元的 server 區塊
  4. 若後置 * server_name 仍沒有相符的,再評估使用正規表達式 server_name 的 server 區塊(在名稱前面使用 ~ 表示)
  5. 如果沒有找到正則表達式匹配,則選擇該 IP 和 Port 的預設 server 區塊

Ngnix 預設將第一個 443 server 區塊視為預設的 443 server 區塊,由於我只有定義一個 server 區塊給 ASP.NET Core,區塊。當接收到使用 IP 的 URL 時,會落入第 5 種狀況,因此被導向 ASP.NET Core Docker。

知道問題所在,要解決不難。我們可另外宣告一個 server 區塊,server_name 定義為 _,listen 部分加上 default_server (或是讓此區塊被優先讀取成為第一個區塊,但我建議明確加註,語意清楚不易出錯)。由於我想完全禁止用 IP 存取不做任何導向,故設定 return 444 切斷連線,並指定 ssl_reject_handshake 避免 TLS 憑證外露,攻擊者由憑證取得網站 DNS 名稱,造成資訊外流。

server {
   listen              443 ssl http2 default_server;
   server_name         _;
   ssl_reject_handshake on;
   return               444;
}

server {
    listen              443 ssl http2;
    server_name         private-web.nobody.know.com;
    ssl_certificate     /etc/letsencrypt/live/private-web.nobody.know.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/private-web.nobody.know.com/privkey.pem;

    location / {
        proxy_pass         http://localhost:5000;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection keep-alive;
        proxy_set_header   Host $host;
        proxy_cache_bypass $http_upgrade;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
    }
}

調整完成後,再試著用 IP 連上網站會被無情拒絕,得到 ERR_SSL_UNRECOGNIZED_NAME_ALERT,無法在獲取任何內容,也無法由 TLS 憑證偷窺 DNS 名稱:

對於一般對外網站,主機 IP 及 DNS 名稱不是祕密,被人改用 IP 存取增加風險有限。若如果是私有主機,想謝絕一切打擾窺探,記得調好相關設定。

就醬,又學到一些 Nginx 知識。

2025-07-05 更新:這篇介紹的方法只能減少騷擾,並不能完全防止 DNS 名稱外流。參考:隱藏私人網站 DNS 名稱有多難?

I discovered that my private website, not publicly disclosed, was still being targeted by bots due to Nginx’s default behavior when accessed by IP. By configuring Nginx to reject IP-based access with return 444 and ssl_reject_handshake, I prevented DNS exposure through TLS certificates.


Comments

# by Nginx

我覺得 這個可能跟http redirect有關

# by Setsuna70

其實還有一種可能,如果攻擊者知道你的 Apex Domain,可以透過你的憑證發行紀錄 (Certificate Transparency) 去找出 Sub Domain,就有可能推出 DNS 資訊了。 例如這樣: https://crt.sh/?q=darkthread.net

# by Jeffrey

to Setsuna70,謝謝分享。FB 留言也有讀者提到 CT Log,長見識了~ 一旦向公開 CA 申請過憑證,DNS 名就不是祕密了。

Post a comment