Nginx 在現代網路基礎架構中扮演著關鍵角色,但其設定不當也可能成為攻擊者的目標。我將深入剖析 Nginx 的安全性議題,並提供強化策略,協助您建構更安全的網路環境。

設定 Nginx 時,root 指令至關重要,它指定伺服器提供檔案的基底目錄。設定不當可能導致嚴重安全風險。

以下是一個錯誤的設定範例:

server {
  root /etc/nginx;
  location /hello.txt {
    try_files $uri $uri/ =404;
    proxy_pass http://127.0.0.1:8080/;
  }
}

這個設定缺少 location / { ... } 區塊,導致 root 指令(/etc/nginx)全域套用,影響所有請求,包括根路徑 /。這可能讓攻擊者存取 /etc/nginx 中的敏感檔案,例如 nginx.conf

攻擊情境:利用根目錄位置漏洞

攻擊者可以傳送特定 HTTP 請求來取得敏感檔案:

curl http://example.com/nginx.conf

如果 root 目錄設定更廣泛,例如 /etc,攻擊者甚至可以存取 /etc/passwd/etc/shadow 等檔案。

防禦策略

  • 限制根目錄:root 目錄設定為較不敏感的位置,例如 /var/www/html
  • 定義根目錄位置: 明確定義 location / { ... } 區塊,安全地處理根路徑請求。
  • 使用許可權和存取控制: 確保 Nginx worker 程式無法讀取敏感檔案,或透過檔案系統許可權進行保護。

不安全的路徑限制

Nginx 設定檔通常包含根據路徑的限制,以保護敏感目錄或頁面。然而,Nginx 處理 URL 請求的方式可能存在繞過這些限制的漏洞。

潛在的繞過技術

  • 尾斜線不比對: Nginx 以不同方式處理帶尾斜線和不帶尾斜線的 URL。攻擊者可能利用此行為繞過限制。例如,如果 /admin 被拒絕,但 /admin. 沒有被明確拒絕,則可能被視為不同的路徑。
  • URL 編碼: 透過編碼 URL 的部分內容,攻擊者可能繞過簡單的字串比對。例如,%61dmin 可能被處理為單獨的路徑。
  • 大小寫敏感性: 在特定設定中,大小寫敏感性可能被利用。例如,如果 Nginx 設定為不區分大小寫,則 /Admin 可能繞過 /admin 的限制。
  • 新增 Null 位元組: 在某些伺服器設定中,附加 Null 位元組(%00)可能透過提前終止字串處理來繞過限制。

攻擊情境:利用路徑限制繞過

假設有一個限制存取 /admin 頁面的設定:

location = /admin {
  deny all;
}
location = /admin/ {
  deny all;
}

攻擊者可以嘗試使用 /admin.%61dmin/Admin 等變體來繞過限制。

防禦策略

  • 明確拒絕所有變體: 使用正規表示式或其他方法明確拒絕所有可能的繞過路徑。
  • 正確解碼 URL: 確保伺服器正確解碼 URL,防止編碼字元繞過限制。
  • 統一大小寫處理: 根據需求設定大小寫敏感性,並確保一致性。
  • 過濾 Null 位元組: 在後端系統中過濾 Null 位元組,防止其被用於繞過限制。

圖表示例:展示路徑繞過攻擊

  graph LR
    Applied[Applied]
    B[B]
    Bypass[Bypass]
    Restriction[Restriction]
A[Client Request: /admin.] --> B{Nginx Path Matching}
B -- Bypass Restriction --> C[Access Granted]
B -- Restriction Applied --> D[Access Denied]

此圖表說明瞭客戶端如何透過請求 /admin. 來繞過 Nginx 的路徑限制,最終獲得存取許可權。

(後續內容將繼續探討其他 Nginx 安全性議題,並提供相應的防禦策略和最佳實務。)

## 解讀 Nginx 設定檔的安全性陷阱與防禦策略

身為一位資深技術工作者,我經常在實際專案中看到因為 Nginx 設定檔的錯誤組態而導致的安全性漏洞。這些錯誤看似微小,卻可能造成嚴重的安全風險。這篇文章將探討 Nginx 設定檔中常見的陷阱,包含路徑繞過、不安全的變數使用、正規表示式漏洞以及後端原始回應洩露等問題,並提供相應的防禦策略和最佳實踐。

### 路徑限制繞過攻擊與防禦

Nginx 的路徑限制功能旨在阻止未授權的存取,但如果設定不當,攻擊者可能會利用各種技巧繞過這些限制。以下是一些常見的攻擊手法:

1. **URL 編碼繞過:** 攻擊者可以使用 URL 編碼,例如 `curl http://example.com/%61dmin`,嘗試存取 `/admin` 目錄。如果伺服器未正確解碼路徑,就可能繞過限制。

2. **大小寫變換繞過:** 攻擊者可能嘗試使用不同的字母大小寫,例如 `curl http://example.com/ADMIN`,如果伺服器未強制區分大小寫,也可能繞過限制。

3. **增加尾隨點繞過:** 攻擊者可以在路徑後增加點,例如 `curl http://example.com/admin.`,試圖欺騙伺服器將路徑解釋為與 `/admin` 不同。

4. **空字元注入繞過:** 攻擊者可能注入空字元,例如 `curl http://example.com/admin%00/`,試圖混淆伺服器。

以下 Nginx 設定示範瞭如何使用正規表示式來防禦這些繞過技巧:

```nginx
location ~* ^/admin(/|$) {
  deny all;
}

這個設定確保 /admin/admin//Admin 等路徑變體都會被拒絕。此外,確保在套用規則之前,URL 路徑已正規化(已解碼、已轉換為小寫等),並實施嚴格的 URI 處理和過濾。

不安全的變數使用與 HTTP 請求分割

$uri$document_uri 變數會自動解碼 URL 編碼字元,如果直接用於處理使用者輸入,可能導致漏洞。例如,以下設定中,$uri 直接用於重新導向 URL:

location / {
  return 302 https://example.com$uri;
}

如果攻擊者傳送包含 %0d%0a(換行符)的請求,例如 http://localhost/%0d%0aDetectify:%20clrf,伺服器會將其解碼為 \r\n,可能導致 HTTP 回應分割攻擊,注入惡意標頭。

安全的做法是使用 $request_uri,它保留原始請求,包括任何 URL 編碼字元:

location / {
  return 302 https://example.com$request_uri;
}

正規表示式漏洞

不謹慎的正規表示式也可能引入漏洞。例如,以下正規表示式未檢查空格:

location ~ /docs/([^/])? { ... $1 ... } # 存在漏洞

更安全的版本應檢查空格:

location ~ /docs/([^/\s])? { ... $1 ... } # 安全,檢查了空格

或者:

location ~ /docs/(.*)? { ... $1 ... } # 安全,比對 /docs/ 後的任何內容

後端原始回應洩露

Nginx 可以攔截後端回應,遮蔽內部錯誤和敏感資訊。但在處理無效 HTTP 請求時,此機制可能失效,導致後端原始回應洩露。

假設 Nginx 前端連線到一個 uWSGI 應用程式,該應用程式在發生錯誤時會傳回包含敏感資訊的 HTTP 500 回應,例如包含敏感資訊的 Secret-Header 自訂標頭。

def application(environ, start_response):
    start_response('500 Error', [('Content-Type', 'text/html'), ('Secret-Header', 'secret-info')])
    return [b"Secret info, should not be visible!"]

以下 Nginx 設定旨在攔截錯誤並隱藏敏感標頭:

http {
    error_page 500 /html/error.html;
    proxy_intercept_errors on;
}

proxy_intercept_errors on 指令確保當後端傳回 HTTP 狀態碼大於 300 時,Nginx 提供自訂錯誤頁面 /html/error.html,而不是後端的回應。然而,在某些情況下,例如後端回應格式不正確,這個機制可能失效,導致敏感資訊洩露。

總結來說,Nginx 設定檔的安全性至關重要。仔細檢查變數使用、正規表示式和錯誤處理設定,並定期進行安全測試,才能有效防禦潛在的攻擊。善用 圖表可以更清晰地展示系統架構和流程,有助於理解和避免這些安全陷阱。

  graph LR
    B[B]
    Error[Error]
    Intercept[Intercept]
    Invalid[Invalid]
    Leakage[Leakage]
    Request[Request]
    Valid[Valid]
    A[Client Request] --> B{Nginx};
    B -- Valid Request --> C[Backend Application];
    C --> D[Nginx Response];
    D --> A;
    B -- Invalid Request --> E[Backend Error Response];
    E -- Leakage --> A;
    B -- Intercept Error --> F[Custom Error Page];
    F --> A;

上圖展示了 Nginx 如何處理請求和錯誤,以及潛在的洩漏路徑。透過理解這些流程,我們可以更好地組態 Nginx,提升網站的安全性。

Nginx 安全設定的陷阱與最佳實務

身為一位資深系統架構師,我經常使用 Nginx 作為網頁伺服器和反向代理。在建置高可靠度和安全性的網路架構時,Nginx 的彈性和效能表現都相當出色。然而,Nginx 設定的複雜性也可能導致一些容易被忽略的安全漏洞。以下我將分享一些我在實務中遇到的 Nginx 安全設定陷阱,並提供一些避免這些問題的最佳實務。

1. merge_slashes 指令的風險

Nginx 的 merge_slashes 指令預設為開啟,它會將 URL 中多個連續的斜線壓縮成一個。雖然這有助於簡化 URL 處理,但也可能隱藏一些漏洞,例如本地檔案包含(LFI)或路徑遍歷攻擊,尤其當 Nginx 作為反向代理時。

http://example.com//etc/passwd  // merge_slashes 開啟時會被壓縮成 http://example.com/etc/passwd

如果後端應用程式存在 LFI 漏洞,攻擊者可以利用這個行為繞過安全機制。

解決方案: 在 Nginx 設定中關閉 merge_slashes 指令:

http {
  merge_slashes off;
}

2. 惡意回應標頭的防範

某些 HTTP 回應標頭可能改變 Nginx 的行為,例如 X-Accel-RedirectX-Accel-BufferingX-Accel-Charset 等。如果後端伺服器傳送惡意標頭,可能導致安全風險。

例如,後端伺服器傳送以下標頭:

X-Accel-Redirect: /.env

如果 Nginx 的 root 設定為 /,這個標頭可能導致 Nginx 內部重新導向到 .env 檔案,洩露敏感的環境變數。

解決方案: 確保後端伺服器不傳送 X-Accel-Redirect 等敏感標頭,除非明確需要。仔細檢查 Nginx 設定,防止意外的存取。

3. map 指令預設值的設定

map 指令常用於將一個值對映到另一個值,例如控制存取或其他邏輯。但如果未指定預設值,可能導致非預期的行為。

http {
  map $uri $mappocallow {
    /map-poc/public 1;
    /map-poc/private 0;
  }
}

server {
  location /map-poc {
    if ($mappocallow = 0) { return 403; }
    return 200 "Hello. It is a private area: $mappocallow";
  }
}

如果存取 /map-poc/undefined$mappocallow 將未設定,可能繞過安全檢查。

解決方案: 務必在 map 指令中指定預設值:

http {
  map $uri $mappocallow {
    default 0;
    /map-poc/public 1;
    /map-poc/private 0;
  }
}

4. DNS 欺騙漏洞的防護

如果 Nginx 使用外部 DNS 伺服器,存在 DNS 欺騙的風險。

解決方案: 設定 Nginx 使用可信的內部 DNS 伺服器(例如 127.0.0.1):

resolver 127.0.0.1;

此外,確保使用 DNSSEC 驗證 DNS 回應。

5. proxy_pass 和 internal 指令的正確使用

proxy_pass 指令用於將請求轉發到另一個伺服器,而 internal 指令確保某些位置只能在 Nginx 內部存取。如果 internal 指令設定不當,可能允許外部存取敏感的內部位置。

解決方案: 確保內部位置設定正確,與無法從外部存取:

location /internal/ {
  internal;
  proxy_pass http://backend/internal/;
}

location /public/ {
  proxy_pass http://backend/public/;
}

6. proxy_set_header 與 HTTP/2 清文走私攻擊

proxy_set_header 指令可以自訂傳送到代理伺服器的標頭。設定 UpgradeConnection 等標頭時,如果設定不當,可能導致 h2c (HTTP/2 cleartext) 走私攻擊,讓攻擊者繞過 Nginx 的安全檢查,直接與後端伺服器建立連線。

解決方案: 避免直接設定 UpgradeConnection 標頭,讓 Nginx 自動處理。如果需要自訂,務必謹慎設定,並參考官方檔案。

7. 監控與日誌記錄

監控和記錄 Nginx 的請求和回應,特別是錯誤和異常情況,有助於及早發現和應對潛在的攻擊。

Nginx 提供了強大的功能和彈性,但正確的設定至關重要。透過遵循以上最佳實務,可以有效降低安全風險,確保網路架構的安全性。在設定 Nginx 時,務必仔細檢查每個指令的含義和潛在影響,並進行充分的測試和驗證。

  graph LR
    B[B]
    A[Client] --> B{Nginx}
    B --> C[Backend Server]
    D[Attacker] --> B

上圖展示了客戶端、Nginx 和後端伺服器的基本架構,以及攻擊者可能的攻擊路徑。理解這個架構有助於更好地理解 Nginx 的安全設定。

Nginx 設定錯誤:避免升級標頭與連線標頭的誤用

在設定 Nginx 作為反向代理伺服器時,proxy_set_header Upgradeproxy_set_header Connection 指令的使用需要格外謹慎。這些指令允許將客戶端的請求標頭轉發到後端伺服器,但若設定不當,可能導致 HTTP/2 清理文字模式 (h2c) 的升級挾帶攻擊,使攻擊者繞過 Nginx 的安全規則,存取受保護的資源。

漏洞成因分析

問題的核心在於後端伺服器若支援 h2c,攻擊者可以透過偽造的 HTTP/1.1 請求,挾帶 HTTP/2 的設定資訊,誘使後端伺服器將連線升級為 HTTP/2。由於 Nginx 的 proxy_pass 指令僅指定後端伺服器的位址,而非特定路徑,因此升級後的 HTTP/2 連線可以直接與後端伺服器通訊,繞過 Nginx 的路徑限制。

以下列有問題的 Nginx 設定為例:

server {
  listen 443 ssl;
  server_name localhost;
  ssl_certificate /usr/local/nginx/conf/cert.pem;
  ssl_certificate_key /usr/local/nginx/conf/privkey.pem;
  location / {
    proxy_pass http://backend:9999;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;  # 存在風險
    proxy_set_header Connection $http_connection; # 存在風險
  }
  location /flag {
    deny all;
  }
}

此設定中,/flag 路徑被明確禁止存取。然而,由於 UpgradeConnection 標頭被轉發,攻擊者可以利用 h2c 漏洞,直接向後端伺服器的 /flag 路徑傳送請求,繞過 Nginx 的限制。

攻擊流程圖解

  graph LR
    C[C]
A[攻擊者傳送偽造請求] --> B(Nginx 轉發請求);
B --> C{後端伺服器支援 h2c?};
C -- 支援 --> D[連線升級為 HTTP/2];
C -- 不支援 --> E[攻擊失敗];
D --> F[繞過 Nginx 存取 /flag];

攻擊範例

攻擊者可以使用 curl 等工具傳送以下請求:

curl -k -v -X GET https://localhost/ -H "Upgrade: h2c" -H "Connection: Upgrade, HTTP2-Settings"

如果後端伺服器支援 h2c,此請求將會觸發連線升級,使攻擊者能夠繞過 Nginx 的限制。

安全設定建議

為避免此類別攻擊,除非絕對必要,否則應移除或限制 proxy_set_header Upgradeproxy_set_header Connection 指令。

以下提供安全的 Nginx 設定範例:

server {
  listen 443 ssl;
  server_name localhost;
  ssl_certificate /usr/local/nginx/conf/cert.pem;
  ssl_certificate_key /usr/local/nginx/conf/privkey.pem;
  location / {
    proxy_pass http://backend:9999;
    proxy_http_version 1.1;
    # 移除或限制以下標頭,除非絕對必要
    # proxy_set_header Upgrade $http_upgrade;
    # proxy_set_header Connection $http_connection;
  }
  location /flag {
    deny all;
  }
}

如果必須使用 WebSocket 或 HTTP/2 升級,請確保後端伺服器已正確設定安全性,與不會允許濫用這些標頭來存取未經授權的資源。

上述程式碼片段展示瞭如何正確設定 Nginx 反向代理,以避免 h2c 挾帶攻擊。關鍵在於移除或限制 proxy_set_header Upgradeproxy_set_header Connection 指令,防止攻擊者利用這些標頭操控後端伺服器的連線升級。這個設定適用於所有使用 Nginx 作為反向代理的場景,特別是當後端伺服器可能支援 h2c 時,更應提高警覺。