在現今網路環境中,確保 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_certificate和ssl_certificate_key用於指定憑證和金鑰檔案的路徑。ssl_protocols指定 NGINX 支援的 SSL/TLS 協定版本。ssl_ciphers設定 NGINX 支援的加密演算法。ssl_session_cache和ssl_session_timeout用於設定 SSL 連線的快取和逾時時間。
代理伺服器加密
NGINX 可以作為代理伺服器,加密與上游伺服器之間的通訊。您可以使用 proxy_ssl_verify 和 proxy_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 紀元格式的過期時間戳。 - 使用
openssl和tr命令生成並轉換 md5 雜湊。 - 在 Python 中,使用
hashlib和base64函式庫實作相同的功能。
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 時才會進行重新導向。
詳細解析:
X-Forwarded-Proto標頭:用於判斷原始請求的協定(HTTP 或 HTTPS)。- 條件判斷:使用
if陳述式檢查$http_x_forwarded_proto是否等於http。 - 重新導向:如果是
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。
詳細解析:
Strict-Transport-Security標頭:用於啟用 HSTS,確保瀏覽器始終使用 HTTPS。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;
}
# ...
}
詳細解析:
- GeoIP 模組安裝:根據 NGINX 版本(NGINX Plus 或 NGINX Open Source)安裝對應的 GeoIP 模組。
map功能:將客戶端的國家程式碼對映到$country_access變數,用於後續的存取控制。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 網段,或透過基本認證提供正確的使用者名稱和密碼。
詳細解析:
satisfy any:表示只需滿足任一安全驗證方法即可。allow和deny:根據 IP 地址的存取控制。auth_basic:啟用基本認證,需要提供使用者名稱和密碼。
討論
satisfy 指令提供了靈活的安全驗證方式,可以結合多種模組(如 http_access_module、http_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 攻擊。
詳細解析:
limit_req_zone:定義根據客戶端 IP 的速率限制區域,並啟用叢集同步。keyval_zone:建立用於儲存黑名單客戶端 IP 的區域,並設定超時時間。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 緩解功能,透過動態速率限制和自動黑名單機制,有效保護應用免受大規模攻擊。