Nginx 筆記 - 封鎖 IP 存取,防止私人網站 DNS 名稱外流
| | 3 | |
故事是這樣的。我用 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 比對套用規則:
- 首先嘗試找到 server_name 與請求的 Host 標頭完全匹配的 server 區
- 尋找 server_name 前置萬用字元(*)相符合的 server 區塊
- 若沒前置 * server_name 匹配,接著尋找 server_name 後置 * 字元的 server 區塊
- 若後置 * server_name 仍沒有相符的,再評估使用正規表達式 server_name 的 server 區塊(在名稱前面使用 ~ 表示)
- 如果沒有找到正則表達式匹配,則選擇該 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 名就不是祕密了。