結論先講

安全功能盡量放 API Gateway,不要讓每個服務自己做。 Rate limiting 放 nginx 層的效能開銷近乎零,放 Express middleware 會吃掉 5-10% throughput。IP whitelist、OAuth2 驗證、CORS 這些「每個請求都要做的事」,在 Gateway 統一處理一次就好,不要在 5 個服務各做 5 次。


API Gateway 是微服務的大門

外部流量進來的路徑:

用戶 → CDN → Load Balancer → API Gateway → 各個微服務
                                   ↑
                              安全檢查都在這裡做:
                              - Rate Limiting
                              - IP Whitelist / Blacklist
                              - OAuth2 / JWT 驗證
                              - CORS
                              - Request Size Limit
                              - SQL Injection / XSS 過濾

Rate Limiting

放在哪裡:Nginx vs 應用層

根據基礎設施壓測數據,rate limiting 放在不同層的效能差異明顯:

實作位置Throughput 影響延遲影響說明
Nginx(limit_req_zone近乎零< 0.1msC 寫的,在記憶體中操作
Express middleware(express-rate-limit-5~10%+2-5ms每個請求都進 Node.js event loop
Redis-based(跨 instance 共享)-2~3%+1-2ms一次 Redis INCR 操作

Nginx Rate Limiting 設定

http {
    # 定義限流區域:每個 IP 每秒 10 個請求
    limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
 
    # 針對 API 路徑的限流:每個 IP 每秒 5 個請求
    limit_req_zone $binary_remote_addr zone=api_write:10m rate=5r/s;
 
    server {
        # 讀取 API:寬鬆一點
        location /api/ {
            limit_req zone=api burst=20 nodelay;
            proxy_pass http://api-gateway;
        }
 
        # 寫入 API:嚴格一點
        location /api/orders {
            limit_req zone=api_write burst=5 nodelay;
            proxy_pass http://api-gateway;
        }
    }
}

分層 Rate Limiting

Layer 1 — Nginx(粗粒度):
  每個 IP 每秒 100 個請求 → 擋 DDoS

Layer 2 — API Gateway(中粒度):
  每個 API Key 每分鐘 600 個請求 → 擋濫用

Layer 3 — Application(細粒度):
  每個用戶每天只能建立 10 筆訂單 → 業務邏輯限制

不要只做一層。Nginx 擋得住暴力攻擊,但擋不住「同一個用戶每秒打 5 次 API」的合法但濫用的行為。


IP Whitelist / Blacklist

用途

Whitelist(白名單):
  - Webhook endpoint 只允許特定 IP(Stripe callback IP)
  - 管理後台只允許辦公室 IP

Blacklist(黑名單):
  - 已知的攻擊 IP
  - 爬蟲 IP 段

Nginx 設定

# Webhook 只允許 Stripe 的 IP
location /webhooks/stripe {
    allow 3.18.12.63/32;
    allow 3.130.192.0/24;
    deny all;
    proxy_pass http://payment-service;
}
 
# 管理後台只允許辦公室 IP
location /admin/ {
    allow 203.0.113.0/24;    # 辦公室
    allow 10.0.0.0/8;        # VPN
    deny all;
    proxy_pass http://admin-service;
}

注意:IP whitelist 不應該是唯一的安全機制。IP 可以被偽造(X-Forwarded-For),搭配認證使用。


OAuth2 Proxy

把 OAuth2 驗證從每個服務抽出來,統一在 Gateway 做:

沒有 OAuth2 Proxy:
  每個服務都要:解析 JWT → 驗證簽名 → 檢查 scope → 查 user info
  → 5 個服務各寫一次一模一樣的邏輯

有 OAuth2 Proxy:
  Gateway 做一次驗證 → 把 user info 放到 header 轉發給後端服務
  → 後端服務只要讀 header,不需要自己驗證
# nginx + oauth2-proxy
location /api/ {
    auth_request /oauth2/auth;
    auth_request_set $user_id $upstream_http_x_auth_request_user;
    auth_request_set $user_email $upstream_http_x_auth_request_email;
 
    proxy_set_header X-User-ID $user_id;
    proxy_set_header X-User-Email $user_email;
    proxy_pass http://api-services;
}

後端服務拿到的 request 已經帶有 X-User-ID header,不需要自己驗 JWT。


CORS

CORS 問題的本質:
  瀏覽器發 AJAX 請求到不同 domain → 瀏覽器先發 OPTIONS preflight
  → 如果 server 沒有回正確的 CORS header → 瀏覽器擋住請求

放在 Gateway 統一處理

location /api/ {
    # CORS headers
    add_header Access-Control-Allow-Origin "https://myapp.com" always;
    add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE" always;
    add_header Access-Control-Allow-Headers "Authorization, Content-Type" always;
    add_header Access-Control-Max-Age 86400 always;
 
    # Preflight
    if ($request_method = 'OPTIONS') {
        return 204;
    }
 
    proxy_pass http://api-services;
}

不要用 Access-Control-Allow-Origin: * 你的 API 不是公開的(除非你是做 public API 的 SaaS),明確指定允許的 domain。


Kong vs APISIX vs Traefik

功能KongAPISIXTraefik
語言Lua(OpenResty)Lua(OpenResty)Go
效能最高(etcd-based)中高
Plugin 生態最豐富豐富且成長快中等
K8s 整合最好(原生 Ingress)
Rate Limiting內建內建需要 middleware
OAuth2內建 plugin內建 plugin需要 ForwardAuth
學習曲線中等中等
額外延遲1-3ms0.5-2ms1-2ms

怎麼選

只需要 Ingress + 基本安全 → Traefik(K8s 原生、最簡單)
需要豐富 plugin + 企業支援 → Kong(社群最大)
效能敏感 + 自定義 plugin → APISIX(效能最好)
不想多一層 → 直接用 Nginx + 手寫 config(最輕量)

常見的坑

1. Rate Limit 沒考慮分散式

你有 3 台 API Gateway → 每台各自計算 rate limit
→ 用戶實際上可以打 3 倍的量

解法:用 Redis 做共享計數器(Redis INCR + EXPIRE)

2. CORS 設定被服務覆蓋

Gateway 設了 CORS header → 後端服務又加了一次
→ 瀏覽器收到重複的 CORS header → 某些瀏覽器會報錯

解法:CORS 只在一個地方設,後端服務不要再加

3. Gateway 變成單點故障

所有流量都過 Gateway → Gateway 掛了就全掛
解法:
  - Gateway 至少 2 個 instance(HA)
  - 前面放 Load Balancer
  - 設定 health check + 自動重啟

下一篇

開發者體驗 — 安全搞定了,回到日常開發的痛點:本地跑 5 個微服務 + 各種 middleware,RAM 直接爆掉。怎麼在本地高效開發微服務?


本系列文章

完整 68 篇目錄見 系列首頁

← 上一篇:微服務安全(一):服務間認證 mTLS vs JWT vs API Key → 下一篇:開發者體驗(一):本地跑 5 個微服務吃掉所有 RAM