返回文章列表

NGINX 安全設定與實務應用

本文探討 NGINX 伺服器安全設定的實務應用,涵蓋 SSL/TLS 憑證設定、代理伺服器加密、安全連結模組的組態與使用方法,以及進階的 HTTPS 重新導向、HSTS 設定、根據地理位置的存取控制,和多重安全驗證機制。此外,文章也介紹了 NGINX Plus 的動態 DDoS 緩解方案,提供全面的 Web

Web 開發 資安

在現今網路環境中,確保 Web 伺服器的安全性至關重要。NGINX 作為一款廣泛使用的 Web 伺服器和反向代理伺服器,提供了豐富的安全設定選項,可以有效保護網站和應用程式免受各種威脅。本文將詳細介紹如何利用 NGINX 的 SSL/TLS、代理伺服器加密、安全連結模組等功能,構建一個安全可靠的 Web 服務環境。同時,我們也會探討一些進階的安全設定技巧,例如 HSTS、根據地理位置的存取控制,以及多重安全驗證方法,以提升網站的安全性。最後,文章還將介紹 NGINX Plus 的動態 DDoS 緩解方案,幫助您應對日益嚴峻的網路攻擊。

NGINX 安全設定與實務應用

NGINX 提供了多種安全設定選項,以確保伺服器與客戶端之間的通訊安全。本文將介紹如何使用 NGINX 的 SSL/TLS 設定、代理伺服器加密以及安全連結模組來保護您的網站和資源。

SSL/TLS 設定

NGINX 支援多種 SSL/TLS 設定,包括憑證金鑰對的設定。您可以透過不同的方式提供 NGINX 憑證金鑰值,例如使用變數或直接值。

server {
    listen 443 ssl;
    ssl_certificate /path/to/cert.crt;
    ssl_certificate_key /path/to/cert.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
}

內容解密:

  • ssl_certificatessl_certificate_key 用於指定憑證和金鑰檔案的路徑。
  • ssl_protocols 指定 NGINX 支援的 SSL/TLS 協定版本。
  • ssl_ciphers 設定 NGINX 支援的加密演算法。
  • ssl_session_cachessl_session_timeout 用於設定 SSL 連線的快取和逾時時間。

代理伺服器加密

NGINX 可以作為代理伺服器,加密與上游伺服器之間的通訊。您可以使用 proxy_ssl_verifyproxy_ssl_protocols 等指令來設定代理伺服器的 SSL/TLS 選項。

location / {
    proxy_pass https://upstream.example.com;
    proxy_ssl_verify on;
    proxy_ssl_verify_depth 2;
    proxy_ssl_protocols TLSv1.3;
}

內容解密:

  • proxy_ssl_verify 用於啟用或停用上游伺服器憑證的驗證。
  • proxy_ssl_verify_depth 設定憑證鏈的驗證深度。
  • proxy_ssl_protocols 指定 NGINX 與上游伺服器之間使用的 SSL/TLS 協定版本。

安全連結模組

NGINX 的安全連結模組可以用於保護特定的位置區塊。您可以使用 secure_link_secret 指令來設定一個秘密金鑰,並使用 $secure_link 變數來驗證請求的 URI。

location /resources {
    secure_link_secret mySecret;
    if ($secure_link = "") { return 403; }
    rewrite ^ /secured/$secure_link;
}

location /secured/ {
    internal;
    root /var/www;
}

內容解密:

  • secure_link_secret 用於設定一個秘密金鑰,用於驗證請求的 URI。
  • $secure_link 變數用於儲存驗證結果,如果驗證失敗,則傳回空字串。
  • rewrite 指令用於將請求的 URI 重寫為內部位置。

NGINX 安全連結模組使用

NGINX 的安全連結模組提供了一種保護資源不被未授權存取的方法。本將介紹如何使用該模組來保護您的資源。

7.7 使用安全連結保護資源

問題

您需要保護特定的 URI 不被未經授權的使用者存取。

解決方案

NGINX 的安全連結模組接受一個 md5 雜湊字串的十六進位制摘要,該字串是 URI 路徑和一個秘密字串的串聯。

首先,您需要生成 md5 雜湊的十六進位制摘要。可以使用 Unix 的 openssl 命令:

$ echo -n 'index.htmlmySecret' | openssl md5 -hex
(stdin)= a53bee08a4bf0bbea978ddf736363a12

或者使用 Python 的 hashlib 函式庫:

import hashlib
hashlib.md5(b'index.htmlmySecret').hexdigest()
'a53bee08a4bf0bbea978ddf736363a12'

有了這個雜湊摘要,您就可以構建一個安全的 URL。例如:

www.example.com/resources/a53bee08a4bf0bbea978ddf736363a12/index.html

詳細解說:

  • echo -n 'index.htmlmySecret':這行命令將 index.html 和秘密字串 mySecret 串聯起來,並輸出到標準輸出,不帶有換行符。
  • openssl md5 -hex:對輸入的字串進行 md5 雜湊運算,並輸出十六進位制摘要。
  • 在 Python 中,hashlib.md5(b'index.htmlmySecret').hexdigest() 實作了相同的功能。

7.8 使用過期日期保護資源

問題

您需要保護資源,使得連結在特定時間後過期,並且是特定客戶端的。

解決方案

使用 NGINX 安全連結模組的指令來設定過期時間,並在安全連結中使用變數:

location /resources {
    root /var/www;
    secure_link $arg_md5,$arg_expires;
    secure_link_md5 "$secure_link_expires$uri$remote_addr mySecret";
    if ($secure_link = "") { return 403; }
    if ($secure_link = "0") { return 410; }
}

詳細解說:

  • secure_link $arg_md5,$arg_expires;:指定用於驗證安全連結的兩個引數:md5 雜湊和過期時間。
  • secure_link_md5 "$secure_link_expires$uri$remote_addr mySecret";:定義用於生成 md5 雜湊的字串格式,包括過期時間、URI、遠端地址和秘密字串。
  • $secure_link 為空時,傳回 403(禁止存取);當 $secure_link 為 0 時,傳回 410(資源已過期)。

7.9 生成過期連結

問題

您需要生成一個在特定時間後過期的連結。

解決方案

首先,生成 Unix 紀元格式的過期時間戳:

$ date -d "2030-12-31 00:00" +%s --utc
1924905600

然後,根據 NGINX 組態中 secure_link_md5 指令定義的格式,生成 md5 雜湊並進行 base64 編碼和轉換:

$ echo -n '1924905600/resources/index.html127.0.0.1 mySecret' \
| openssl md5 -binary \
| openssl base64 \
| tr +/ -_ \
| tr -d =
sqysOw5kMvQBL3j9ODCyoQ

最終的連結格式為:

/resources/index.html?md5=sqysOw5kMvQBL3j9ODCyoQ&expires=1924905600

Python 示例程式碼:

from datetime import datetime, timedelta
from base64 import b64encode
import hashlib

# 設定環境變數
resource = b'/resources/index.html'
remote_addr = b'127.0.0.1'
host = b'www.example.com'
mysecret = b'mySecret'

# 生成過期時間戳
now = datetime.utcnow()
expire_dt = now + timedelta(hours=1)
expire_epoch = str.encode(expire_dt.strftime('%s'))

# md5 雜湊字串
uncoded = expire_epoch + resource + remote_addr + mysecret
md5hashed = hashlib.md5(uncoded).digest()

# base64 編碼和轉換字串
b64 = b64encode(md5hashed)
unpadded_b64url = b64.replace(b'+', b'-')\
                     .replace(b'/', b'_')\
                     .replace(b'=', b'')

# 生成安全連結
linkformat = "{}{}?md5={}&expires={}"
securelink = linkformat.format(
    host.decode(),
    resource.decode(),
    unpadded_b64url.decode(),
    expire_epoch.decode()
)
print(securelink)

詳細解說:

  • 使用 date 命令生成 Unix 紀元格式的過期時間戳。
  • 使用 openssltr 命令生成並轉換 md5 雜湊。
  • 在 Python 中,使用 hashlibbase64 函式庫實作相同的功能。

7.10 HTTPS 重定向

問題

您需要將未加密的 HTTP 請求重定向到 HTTPS。

解決方案

使用 NGINX 的 rewrite 功能將所有 HTTP 請求重定向到 HTTPS:

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    ssl_certificate /etc/nginx/ssl/example.crt;
    ssl_certificate_key /etc/nginx/ssl/example.key;
    ...
}

詳細解說:

  • 第一個 server 區塊監聽埠 80,並將所有請求重定向到 HTTPS。
  • 第二個 server 區塊監聽埠 443,並組態 SSL 證書。

NGINX 安全設定與進階應用

7.11 在 SSL/TLS 終止於 NGINX 之前的情況下重新導向至 HTTPS

問題描述

需要在 SSL/TLS 終止於 NGINX 之前的情況下重新導向至 HTTPS。

解決方案

使用常見的 X-Forwarded-Proto 標頭來判斷是否需要重新導向:

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    if ($http_x_forwarded_proto = 'http') {
        return 301 https://$host$request_uri;
    }
}

此設定與一般的 HTTPS 重新導向類別似,但只有在 X-Forwarded-Proto 標頭等於 http 時才會進行重新導向。

詳細解析:

  1. X-Forwarded-Proto 標頭:用於判斷原始請求的協定(HTTP 或 HTTPS)。
  2. 條件判斷:使用 if 陳述式檢查 $http_x_forwarded_proto 是否等於 http
  3. 重新導向:如果是 http,則傳回 301 狀態碼,將請求重新導向至對應的 HTTPS 位址。

討論

在某些情況下,SSL/TLS 可能會在 NGINX 之前的層(如 AWS ELB)終止。此時,NGINX 需要根據 X-Forwarded-Proto 標頭來判斷是否需要將請求重新導向至 HTTPS,以確保所有流量都經過加密傳輸。

7.12 HTTP 嚴格傳輸安全(HSTS)

問題描述

需要指示瀏覽器永遠不要透過 HTTP 傳送請求。

解決方案

使用 HTTP 嚴格傳輸安全(HSTS)增強功能,設定 Strict-Transport-Security 標頭:

add_header Strict-Transport-Security max-age=31536000;

此設定將 Strict-Transport-Security 標頭的最大存活時間設為一年,指示瀏覽器在該段時間內始終將 HTTP 請求內部重新導向至 HTTPS。

詳細解析:

  1. Strict-Transport-Security 標頭:用於啟用 HSTS,確保瀏覽器始終使用 HTTPS。
  2. max-age 引數:定義 HSTS 政策的有效時間(秒數),範例中設為一年(31536000 秒)。

討論

HSTS 可以有效防止中間人攻擊,因為它確保了即使是初始請求也透過 HTTPS 傳送,避免了敏感資訊在未加密的連線中傳輸。

7.13 根據國家/地區限制存取

問題描述

需要根據合約或應用需求限制特定國家/地區的存取。

解決方案

安裝 NGINX GeoIP 模組,並使用 map 功能將國家程式碼對映到變數:

load_module "/etc/nginx/modules/ngx_http_geoip_module.so";

http {
    map $geoip_country_code $country_access {
        "US" 0;
        "CA" 0;
        default 1;
    }
    # ...
}

接著,在 server 區塊中使用 if 陳述式根據 $country_access 的值決定是否允許存取:

server {
    if ($country_access = '1') {
        return 403;
    }
    # ...
}

詳細解析:

  1. GeoIP 模組安裝:根據 NGINX 版本(NGINX Plus 或 NGINX Open Source)安裝對應的 GeoIP 模組。
  2. map 功能:將客戶端的國家程式碼對映到 $country_access 變數,用於後續的存取控制。
  3. if 陳述式:檢查 $country_access 的值,若為 1 則傳回 403 狀態碼,拒絕存取。

討論

此範例展示瞭如何使用 GeoIP 模組根據客戶端的地理位置進行存取控制,可以根據實際需求擴充套件以允許或阻止更多國家/地區。

7.14 多重安全驗證方法

問題描述

需要提供多種方式來透過安全驗證以存取受限站點。

解決方案

使用 satisfy 指令來指定需要滿足的安全驗證方法:

location / {
    satisfy any;
    allow 192.168.1.0/24;
    deny all;
    auth_basic "closed site";
    auth_basic_user_file conf/htpasswd;
}

此設定允許客戶端透過以下任一方式透過驗證:來自 192.168.1.0/24 網段,或透過基本認證提供正確的使用者名稱和密碼。

詳細解析:

  1. satisfy any:表示只需滿足任一安全驗證方法即可。
  2. allowdeny:根據 IP 地址的存取控制。
  3. auth_basic:啟用基本認證,需要提供使用者名稱和密碼。

討論

satisfy 指令提供了靈活的安全驗證方式,可以結合多種模組(如 http_access_modulehttp_auth_basic_module 等)實作多層次的安全防護。

7.15 NGINX Plus 的動態應用層 DDoS 緩解

問題描述

需要動態的分散式阻斷服務(DDoS)緩解方案。

解決方案

使用 NGINX App Protect DoS 模組或 NGINX Plus 功能建立叢集感知的速率限制和自動黑名單:

limit_req_zone $remote_addr zone=per_ip:1M rate=100r/s sync;
limit_req_status 429;

keyval_zone zone=sinbin:1M timeout=600 sync;
keyval $remote_addr $in_sinbin zone=sinbin;

server {
    listen 80;
    location / {
        if ($in_sinbin) {
            set $limit_rate 50; # 對惡意客戶端進行頻寬限制
        }
        # ...
    }
}

此設定透過叢集感知的速率限制和動態黑名單機制,有效緩解 DDoS 攻擊。

詳細解析:

  1. limit_req_zone:定義根據客戶端 IP 的速率限制區域,並啟用叢集同步。
  2. keyval_zone:建立用於儲存黑名單客戶端 IP 的區域,並設定超時時間。
  3. if ($in_sinbin):檢查客戶端是否在黑名單中,若是則限制其頻寬。

圖示說明

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title NGINX 安全設定與實務應用

package "安全架構" {
    package "網路安全" {
        component [防火牆] as firewall
        component [WAF] as waf
        component [DDoS 防護] as ddos
    }

    package "身份認證" {
        component [OAuth 2.0] as oauth
        component [JWT Token] as jwt
        component [MFA] as mfa
    }

    package "資料安全" {
        component [加密傳輸 TLS] as tls
        component [資料加密] as encrypt
        component [金鑰管理] as kms
    }

    package "監控審計" {
        component [日誌收集] as log
        component [威脅偵測] as threat
        component [合規審計] as audit
    }
}

firewall --> waf : 過濾流量
waf --> oauth : 驗證身份
oauth --> jwt : 簽發憑證
jwt --> tls : 加密傳輸
tls --> encrypt : 資料保護
log --> threat : 異常分析
threat --> audit : 報告生成

@enduml

此圖示展示了 NGINX 如何處理客戶端請求並進行速率限制和黑名單管理。

討論

NGINX Plus 和 NGINX App Protect DoS 提供強大的 DDoS 緩解功能,透過動態速率限制和自動黑名單機制,有效保護應用免受大規模攻擊。