背景
- 公有云服務器 http 服務 80端口,想做到安全訪問
- 無須HTTPS + 客戶端證書
- 方便、快捷、安全
SSH 隧道 + 本地代理
使用 SSH 隧道將 HTTP 服務“隱藏”在 SSH 之后:
# 客戶端建立隧道(將本地 8080 轉發到服務器的 80 端口)
ssh -L 8080:localhost:80 user@your-server -i ~/.ssh/id_ed25519# 然后訪問本地端口(需先通過 SSH 認證)
curl http://localhost:8080
- 適用場景:臨時授權訪問,不適合公開服務。
- 在公有云的安全組中可以禁止80端口,也不影響在本地訪問 http://localhost:8080
因為 SSH 隧道(端口轉發)的流量是通過 SSH 協議(默認 22 端口) 傳輸的,與目標服務(如 HTTP 的 80 端口)無關。
其他方式
? 方案一:使用客戶端證書(TLS Mutual Authentication)
這是最接近 SSH 公鑰認證的做法。
? 原理(TLS 雙向認證):
- 客戶端持有私鑰 + 客戶端證書
- 服務端信任某個 CA(或特定證書)
- 在 TLS 握手階段,客戶端用私鑰簽名一個隨機值,服務端驗證客戶端證書和簽名
- 驗證通過后,允許訪問
? 實現步驟(以 Nginx 為例):
1. 創建自簽 CA:
openssl genrsa -out ca.key 4096
openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.crt
2. 為客戶端生成證書:
openssl genrsa -out client.key 2048
openssl req -new -key client.key -out client.csr
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365 -sha256
3. 配置 Nginx 啟用客戶端認證:
server {listen 443 ssl;server_name example.com;ssl_certificate /path/to/server.crt;ssl_certificate_key /path/to/server.key;ssl_client_certificate /path/to/ca.crt; # 信任的CAssl_verify_client on;location / {root /var/www/html;}
}
4. 客戶端訪問時使用瀏覽器導入證書:
- Chrome / Firefox 支持在“證書管理”中導入
.crt
和.key
文件 - 或用 curl 測試:
curl -v --cert client.crt --key client.key https://example.com/
? 方案二:基于 JWT(非對稱簽名)模擬公鑰認證
如果 TLS 客戶端證書太重,你可以用 JWT 模擬“公鑰簽名驗證”機制。
? 原理:
- 客戶端持有私鑰,每次請求前用私鑰簽名一個 payload,生成 JWT
- 服務端用已知公鑰驗證簽名,確認身份
? 實現邏輯(偽代碼):
客戶端(Python 示例):
import jwt
from datetime import datetime, timedeltaprivate_key = open("client.key").read()payload = {"sub": "client1","exp": datetime.utcnow() + timedelta(minutes=5)
}token = jwt.encode(payload, private_key, algorithm="RS256")
# 在 header 中發送
headers = {"Authorization": f"Bearer {token}"}
服務端驗證(Python Flask 示例):
from flask import request
import jwtpublic_key = open("client.pub").read()token = request.headers.get("Authorization").split()[1]try:payload = jwt.decode(token, public_key, algorithms=["RS256"])print("Client ID:", payload["sub"])
except jwt.ExpiredSignatureError:return "Token expired", 401
except jwt.InvalidTokenError:return "Invalid token", 403
這種方式適合你已有 Web 架構,且不想涉及證書分發的問題。
? 方案三:基于公鑰的 HTTP 簽名(Http Signature)
這是類似于 AWS、GitHub Webhook 的請求簽名機制。
? 原理:
- 請求者用私鑰簽名 HTTP 請求(方法、時間戳、body 等)
- 服務端使用公鑰驗證簽名是否匹配
? 適用場景:
- REST API 鑒權
- 高安全需求的 webhook 回調
- 可接入現有 Web 服務中間件
? 示例實現:
參考規范:
- IETF HTTP Signatures Draft
- 實現庫如 Node 的
http-signature
, Python 的requests-http-signature
🧠 總結對比
方案 | 是否基于公鑰 | 瀏覽器支持 | 安全性 | 部署復雜度 |
---|---|---|---|---|
TLS 客戶端證書 | ? 是 | ? | ★★★★★ | 高 |
JWT + 私鑰簽名 | ? 是 | ?(僅后端) | ★★★★ | 中 |
HTTP 請求簽名(HttpSig) | ? 是 | ? | ★★★★ | 中 |
HTTP Basic / Cookie / 密碼 | ? 否 | ? | ★★ | 低 |