NGINX 身分驗證機制對於保障 Web 應用程式安全至關重要。本文不僅涵蓋基本驗證和子請求驗證等常見方法,更探討了根據 JWT 和 SAML 的現代驗證方案。這些方法各有優劣,適用於不同的應用場景。例如,基本驗證適用於簡單的存取控制,而 JWT 和 SAML 則更適合複雜的單一登入(SSO)情境。此外,文章也詳細說明瞭如何設定 CORS,以允許跨域資源分享,以及如何正確組態 SSL/TLS 加密,確保資料傳輸安全。最後,文章還介紹瞭如何利用 NGINX 設定根據 IP 位址的存取控制,進一步強化 Web 應用程式的安全性。
使用NGINX進行身分驗證的方法
NGINX提供了多種身分驗證方法,以確保只有授權使用者可以存取特定的資源或服務。這些方法包括基本身分驗證、身份驗證子請求以及使用JSON Web Token(JWT)進行驗證。
基本身分驗證
基本身分驗證是一種簡單的身分驗證方法,透過要求使用者輸入使用者名稱和密碼來驗證其身份。NGINX支援這種驗證方法,可以用於保護整個NGINX主機、特定的虛擬伺服器或特定的位置區塊。
設定基本身分驗證
要設定基本身分驗證,需要在NGINX組態檔案中使用auth_basic和auth_basic_user_file指令。auth_basic指令指定了驗證域,而auth_basic_user_file指令則指定了包含使用者憑據的檔案路徑。
location /protected/ {
auth_basic "Restricted Area";
auth_basic_user_file /etc/nginx/.htpasswd;
}
基本身分驗證的工作原理
當使用者嘗試存取受保護的資源時,NGINX會傳回一個401 Unauthorized HTTP狀態碼,並在回應頭中包含WWW-Authenticate欄位,提示使用者輸入使用者名稱和密碼。使用者輸入憑據後,瀏覽器會將使用者名稱和密碼以base64編碼的形式傳送到伺服器。伺服器然後解碼並驗證這些憑據,如果正確,則允許使用者存取資源。
#### 內容解密:
auth_basic指令用於啟用基本身分驗證並指定驗證域。auth_basic_user_file指令指定包含使用者憑據的檔案。- 使用者名稱和密碼以base64編碼,但這並不安全,因此建議與HTTPS一起使用,以加密傳輸的憑據。
身份驗證子請求
對於需要與第三方身份驗證系統整合的情況,NGINX提供了身份驗證子請求功能。這允許NGINX在處理請求之前,先向身份驗證服務傳送一個子請求,以驗證使用者的身份。
設定身份驗證子請求
要使用身份驗證子請求,需要在NGINX組態檔案中設定auth_request指令,指定一個內部位置來處理身份驗證子請求。
location /private/ {
auth_request /auth;
auth_request_set $auth_status $upstream_status;
}
location = /auth {
internal;
proxy_pass http://auth-server;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}
身份驗證子請求的工作原理
當使用者傳送請求時,NGINX會先向指定的身份驗證服務傳送一個子請求。如果子請求傳回200 OK狀態碼,則允許原始請求繼續;否則,NGINX會傳回與子請求相同的狀態碼給使用者。
#### 內容解密:
auth_request指令指定了用於身份驗證的子請求的位置。auth_request_set指令允許從身份驗證子請求的回應中設定變數。- 身份驗證服務可以根據需要檢查請求的任何方面,包括頭部和URI。
使用JSON Web Token(JWT)進行驗證
NGINX Plus支援使用JSON Web Token(JWT)進行身份驗證。JWT是一種安全的令牌格式,可以用來在各方之間安全地傳遞資訊。
設定JWT驗證
要使用JWT驗證,需要在NGINX Plus組態檔案中設定auth_jwt指令。
location /api/ {
auth_jwt "api";
auth_jwt_key_file conf/keys.json;
}
JWT驗證的工作原理
NGINX Plus會檢查請求中的JWT令牌,驗證其簽名,並將令牌中的宣告和頭部作為變數提供給組態。
#### 內容解密:
auth_jwt指令啟用了JWT驗證並指定了驗證域。auth_jwt_key_file指令指定了包含JSON Web Key(JWK)的檔案,用於驗證JWT簽名。- NGINX Plus支援多種簽名演算法,包括HS256、RS256和ES256。
建立JSON Web Key(JWK)
要使用JWT驗證,需要有一個JSON Web Key(JWK)檔案。JWK是一種標準格式,用於表示可以用於簽署和驗證JWT的加密金鑰。
JWK檔案範例
{
"keys": [
{
"kty": "oct",
"kid": "0001",
"k": "OctetSequenceKeyValue"
},
{
"kty": "EC",
"kid": "0002",
"crv": "P-256",
"x": "XCoordinateValue",
"y": "YCoordinateValue",
"d": "PrivateExponent",
"use": "sig"
}
]
}
#### 內容解密:
- JWK檔案包含一個或多個金鑰物件,每個物件代表一個可以用於JWT簽名或加密的金鑰。
kty欄位指定了金鑰型別,例如oct表示對稱金鑰,EC表示橢圓曲線金鑰。- 其他欄位根據金鑰型別不同而有所不同,例如
k用於對稱金鑰,而x、y、d等用於橢圓曲線金鑰。
綜上所述,NGINX提供了多種靈活的身分驗證方法,可以根據具體需求選擇最合適的方式來保護資源和服務。無論是基本身分驗證、身份驗證子請求還是JWT驗證,NGINX都能提供強大的支援,確保只有授權使用者可以存取敏感資訊。
JSON Web Key(JWK)與 NGINX Plus 的應用
JWK 格式與型別
JSON Web Key(JWK)是一種用於表示加密金鑰的 JSON 格式。RFC 標準定義了 JWK 的格式和多種金鑰型別,包括 Octet Sequence(oct)、Elliptic Curve(EC)和 RSA。
以下是一個 JWK 檔案的範例:
{
"keys": [
{
"kty": "oct",
"kid": "0001",
"k": "HMAC金鑰"
},
{
"kty": "EC",
"kid": "0002",
"crv": "P-256",
"x": "X座標",
"y": "Y座標",
"d": "私鑰"
},
{
"kty": "RSA",
"kid": "0003",
"n": "模數",
"e": "指數",
"d": "私鑰"
}
]
}
內容解密:
kty屬性表示金鑰型別。kid屬性是金鑰 ID,用於識別不同的金鑰。- 不同型別的金鑰有不同的屬性,例如 EC 金鑰有
crv、x、y和d屬性,而 RSA 金鑰有n、e和d屬性。
使用 NGINX Plus 驗證 JSON Web Tokens(JWT)
NGINX Plus 可以使用 JWT 模組來驗證 JWT。以下是一個範例設定:
location /private/ {
auth_jwt "Google Oauth" token=$cookie_auth_token;
auth_jwt_key_file /etc/nginx/google_certs.jwk;
}
內容解密:
auth_jwt指令用於啟用 JWT 驗證。token=$cookie_auth_token指定了 JWT 的位置,在此例中是auth_tokencookie。auth_jwt_key_file指定了用於驗證 JWT 簽名的 JWK 檔案路徑。
自動取得和快取 JSON Web Key Sets(JWKS)
NGINX Plus 可以使用 auth_jwt_key_request指令自動從身份提供者取得 JWKS 並快取。以下是一個範例設定:
proxy_cache_path /data/nginx/cache levels=1 keys_zone=foo:10m;
server {
# ...
location / {
auth_jwt "closed site";
auth_jwt_key_request /jwks_uri;
}
location = /jwks_uri {
internal;
proxy_cache foo;
proxy_pass https://idp.example.com/keys;
}
}
內容解密:
auth_jwt_key_request指令指示 NGINX Plus 從內部子請求取得 JWKS。- 子請求被導向
/jwks_uri,該位置代理請求到身份提供者。 - 使用快取來限制對身份提供者的請求頻率。
將 NGINX Plus 組態為 SAML 身份提供者的服務提供者
NGINX Plus 可以與 SAML 身份提供者整合以保護資源。以下是一個範例設定:
# 安裝 njs 模組
$ apt install nginx-plus-module-njs
# 在 nginx.conf 中載入 njs 模組
load_module modules/ngx_http_js_module.so;
內容解密:
- 需要安裝 njs 模組來支援 SAML 身份驗證。
- 載入 njs 模組需要在
nginx.conf中新增相應的設定。
NGINX 與 SAML 整合實作單一登入(SSO)驗證
NGINX Plus 提供了一種根據 JavaScript 的 SAML 服務提供者(SP)解決方案,用於保護資源並實作單一登入(SSO)。本解決方案結合 NGINX JavaScript 模組和 NGINX Plus 鍵值儲存模組,實作了 SAML SP 功能。
設定步驟
載入 NGINX JavaScript 模組
load_module modules/ngx_http_js_module.so;下載並設定 SAML JavaScript 和 NGINX Plus 組態檔案
- 下載 SAML 解決方案:
$ wget https://github.com/nginxinc/nginx-saml/archive/refs/heads/main.zip -O nginx-saml-main.zip - 解壓縮:
$ unzip nginx-saml-main.zip - 移動組態檔案:
$ mv nginx-saml-main/* /etc/nginx/conf.d/
- 下載 SAML 解決方案:
更新組態檔案
saml_sp_configuration.conf:設定服務提供者(SP)和身份提供者(IdP)的相關組態。- 修改
$saml_sp_字首的變數以符合 SP 組態。 - 修改
$saml_idp_字首的變數以符合 IdP 組態。 - 設定登出後重定向的 URI。
- 修改
frontend.conf:範例反向代理組態,使用 SAML 解決方案保護資源。- 在
server組態中包含saml_sp.server_conf。 - 使用
error_page指令啟動 SAML SP 流程。
- 在
saml_sp.server_conf:處理 IdP 回應的 NGINX 組態,通常不需要修改。saml_sp.js:執行 SAML 認證的 JavaScript 程式碼,無需修改。
設定檔解說
此處使用了多個設定檔來達成SAML SSO的功能:
saml_sp_configuration.conf:主要用於定義SP和IdP的組態,使用map區塊來進行多重組態。frontend.conf:用於反向代理組態,保護特定資源。saml_sp.server_conf:用於處理來自IdP的回應,一般無需變更。saml_sp.js:SAML驗證的核心JavaScript程式碼,無需修改。
SAML SSO 工作流程
- SP 和 IdP 組態:透過
saml_sp_configuration.conf組態 SP 和 IdP。 - 啟動 SP 流程:當 NGINX Plus 沒有為使用者儲存會話時,使用
error_page指令啟動 SP 流程,將使用者重定向到 IdP。 - 驗證和會話儲存:IdP 驗證使用者後,將 SAML 回應發送回 NGINX Plus。NGINX Plus 驗證回應並將會話儲存在鍵值儲存中。
- Cookie 設定:將 SAML 回應的鍵以 Cookie 形式傳回給客戶端,用於後續請求。
單一登出(SLO)功能
SLO 功能允許使用者透過單一操作登出所有 SP 和 IdP。可以透過設定 $saml_idp_slo_url 為空字串來停用 SLO 功能。
SLO 工作流程
- SP 發起的登出:NGINX Plus 向 IdP 傳送 LogoutRequest,IdP 終止使用者會話並回傳 LogoutResponse。
- IdP 發起的登出:IdP 向 NGINX Plus 傳送 LogoutRequest,NGINX Plus 刪除相關會話並回傳 LogoutResponse。
安全控制
本章節討論了多種使用 NGINX 加固 Web 應用程式安全的方法,包括根據 IP 位址的存取控制。
根據 IP 位址的存取控制
location /admin/ {
deny 10.0.0.1;
allow 10.0.0.0/20;
deny all;
}
此組態限制了對 /admin/ 路徑的存取,只允許來自 10.0.0.0/20 網段的 IP 位址,但拒絕 10.0.0.1。
NGINX 存取控制與安全設定
7.1 利用 NGINX 進行存取控制
問題描述
需要對特定資源進行存取控制,限制特定 IP 地址或網段的存取許可權。
解決方案
使用 NGINX 的 allow 和 deny 指令來控制存取:
location / {
deny 10.0.0.1;
allow 10.0.0.0/20;
allow 2001:0db8::/32;
deny all;
}
內容解密:
deny 10.0.0.1;- 禁止特定的 IPv4 地址10.0.0.1存取。allow 10.0.0.0/20;- 允許10.0.0.0到10.0.15.255的 IPv4 地址範圍存取。allow 2001:0db8::/32;- 允許特定的 IPv6 網段2001:0db8::/32存取。deny all;- 禁止所有其他未被明確允許的 IP 地址存取。
這些指令按照順序檢查,直到找到匹配的規則為止。
7.2 啟用跨來源資源共用(CORS)
問題描述
需要允許來自不同網域的資源被瀏覽器使用。
解決方案
使用 NGINX 設定 CORS 頭部資訊:
map $request_method $cors_method {
OPTIONS 11;
GET 1;
POST 1;
default 0;
}
server {
location / {
if ($cors_method ~ '1') {
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS';
add_header 'Access-Control-Allow-Origin' '*.example.com';
add_header 'Access-Control-Allow-Headers' 'DNT,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
}
if ($cors_method = '11') {
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Content-Type' 'text/plain; charset=UTF-8';
add_header 'Content-Length' 0;
return 204;
}
}
}
內容解密:
- 使用
map將請求方法對映到特定的變數值,簡化 CORS 設定。 - 對
GET和POST請求,設定允許的方法、來源和頭部資訊。 - 對
OPTIONS請求,傳回預檢請求的結果,包含快取時間等資訊。
7.3 使用者端加密
問題描述
需要在 NGINX 伺服器和使用者端之間加密傳輸的資料。
解決方案
使用 SSL/TLS 模組加密傳輸:
http {
server {
listen 8443 ssl;
ssl_certificate /etc/nginx/ssl/example.crt;
ssl_certificate_key /etc/nginx/ssl/example.key;
}
}
內容解密:
listen 8443 ssl;- 設定伺服器監聽在 8443 連線埠,並啟用 SSL/TLS。ssl_certificate- 指定伺服器的憑證檔案。ssl_certificate_key- 指定伺服器的私鑰檔案。
7.4 高階使用者端加密設定
問題描述
需要對 SSL/TLS 交握過程進行更細緻的控制。
解決方案
使用 NGINX 的 SSL/TLS 相關指令進行詳細設定:
http {
server {
listen 8443 ssl;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_certificate /etc/nginx/ssl/example.crt;
ssl_certificate_key /etc/nginx/ssl/example.pem;
ssl_certificate $ecdsa_cert;
ssl_certificate_key data:$ecdsa_key_path;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
}
}
內容解密:
ssl_protocols- 指定接受的 TLS 版本。ssl_ciphers- 設定加密演算法,HIGH:!aNULL:!MD5表示使用高強度加密演算法,並排除不安全的演算法。- 使用多個憑證和私鑰檔案,包括檔案路徑和變數值。
ssl_session_cache和ssl_session_timeout- 設定 SSL/TLS 交握結果的快取。