MCP 伺服器加固指南:完整部署安全
MCP 伺服器部署的完整加固指南——涵蓋 24 項安全檢核清單、Docker 隔離、Nginx 反向代理配置、日誌與監控設定,以及網路政策強制,每個組件皆附可運作的配置。
本頁提供 MCP 伺服器部署的完整、可用於正式環境的加固指南。所展示的每項配置皆為可運作範例,可依環境調整。本指南採縱深防禦:每層提供獨立保護,整合起來處理完整的 MCP 威脅景觀。
24 項 MCP 安全檢核清單
以下 YAML 檢核清單可作為部署閘口——所有項目必須通過才能上正式環境。分為五大類:
認證與授權 (項目 1-6):
- HTTP+SSE 伺服器要求 Bearer token 或 mTLS 認證 (關鍵) — 測試
curl -s -o /dev/null -w '%{http_code}' https://mcp-server/sse,預期 401 - 強制執行 token 輪替政策 (最多 90 天壽命) — 高
- 為每個客戶端配置逐工具授權範圍 — 高
- 最小權限原則:客戶端僅存取所需工具 — 高
- stdio 伺服器驗證父程序身分 — 中
- 逐客戶端逐工具配置速率限制 — 高
輸入驗證 (項目 7-12):
7. 所有工具參數在執行前依類型 schema 驗證 — 關鍵 (對每個參數發送注入載荷,驗證被拒)
8. 任何子程序呼叫皆無 shell=True — 關鍵 (以 grep -r 'shell=True' src/ 偵測)
9. 所有檔案操作防止路徑穿越 — 關鍵 (發送 ../../etc/passwd 驗證被阻擋)
10. 工具描述驗證:無注入樣式 — 高
11. 強制參數長度上限 — 中 (發送 100KB 字串驗證被拒)
12. 所有工具配置輸出大小上限 — 高
隔離與沙箱化 (項目 13-17):
13. MCP 伺服器在 Docker 容器或 namespace 中執行 — 高
14. 容器以非 root 使用者執行 — 關鍵 (docker exec mcp-server whoami 不應為 root)
15. 唯讀根檔案系統,僅有明確可寫入掛載 — 高 (docker exec mcp-server touch /test 應失敗)
16. 所有能力捨棄 (--cap-drop ALL) — 高
17. 網路存取限制至必要端點 — 高
監控與日誌 (項目 18-21): 18. 所有工具呼叫以參數、客戶端身分與時間記錄 — 關鍵 19. 安全事件產生警報 (認證失敗、注入嘗試) — 高 20. Token 預算追蹤與警報已配置 — 高 21. 日誌保留符合合規要求 (至少 90 天) — 中
供應鏈與更新 (項目 22-24):
22. MCP 伺服器套件釘選至特定驗證過版本 — 關鍵
23. 啟動時套件完整性驗證 — 高
24. MCP SDK 版本 >= 1.26.0 (會話隔離修復) — 關鍵 (pip show mcp | grep Version)
自動化檢核清單驗證腳本
Bash 腳本 mcp-security-audit.sh 接受伺服器 URL 與容器名作為參數,維護 PASS/FAIL/WARN 計數。check 函式以彩色輸出打印結果。測試項目包含:
- 項目 1:
curl -s -o /dev/null -w '%{http_code}'對/sse檢查;401/403 為 PASS、連線失敗為 WARN、其他為 FAIL - 項目 8:
docker exec $CONTAINER grep -r "shell=True" /app/排除測試與註解後若有匹配即 FAIL - 項目 9:以 POST 發送
tools/call含path=../../etc/passwd,回應若含denied|blocked|error|traversal則 PASS - 項目 14:
docker exec $CONTAINER whoami非 root 則 PASS - 項目 15:
docker exec $CONTAINER touch /test-write失敗則 PASS (唯讀檔案系統) - 項目 16:
docker inspect --format='{{.HostConfig.CapDrop}}'含all則 PASS - 項目 24:
pip show mcp的版本透過 Pythonpackaging.version.Version比對>= 1.26.0
結束時依 PASS/FAIL/WARN 計數印出摘要,任一 FAIL 則以退出碼 1 結束 (供 CI/CD 閘口使用)。
MCP 伺服器的 Docker 隔離
正式環境 Dockerfile
多階段建置以減少攻擊面。建置階段:基於 python:3.12-slim、pip install --prefix=/install 將依賴與應用程式碼安裝至獨立目錄。執行時階段:新的 python:3.12-slim、以 useradd -r -g mcp --uid=10001 -s /usr/sbin/nologin 建立非 root 使用者 mcp (UID 10001)、從建置階段複製 /install 至 /usr/local、以 --chown=mcp:mcp 複製應用程式碼、建立 /workspace、/var/log/mcp、/var/mcp/config 並設定 750 權限、清理 apt 快取、移除 *.pyc 與 __pycache__、設定安全標籤、USER 10001:10001、HEALTHCHECK 每 30 秒以 urllib.request.urlopen 檢查 /health 端點。
Docker Compose 配置
mcp-server 服務:restart: unless-stopped、read_only: true、tmpfs: /tmp:size=100M,noexec,nosuid,nodev、cap_drop: [ALL]、security_opt: no-new-privileges:true、資源限制 (CPU 1.0、記憶體 512M、PID 100)、保留 (CPU 0.25、記憶體 128M)、卷掛載 mcp-workspace:/workspace:rw、mcp-config:/var/mcp/config:ro、mcp-logs:/var/log/mcp:rw、僅 mcp-internal 網路 (透過 nginx 存取)、環境變數 MCP_LOG_LEVEL=info、MCP_WORKSPACE=/workspace、MCP_HOST=0.0.0.0、MCP_PORT=8080、env_file: .env.mcp (含密鑰,不納入版本控制)、JSON 檔案日誌驅動、50MB 單檔、5 檔輪替。
nginx-proxy 服務:nginx:1.25-alpine、read_only: true、tmpfs /tmp、/var/cache/nginx、/run、僅 NET_BIND_SERVICE 能力、曝露 443、掛載 ./nginx/conf.d、./nginx/certs 唯讀、nginx-logs 可寫、同時在 mcp-external 與 mcp-internal 網路。
promtail 服務:grafana/promtail:latest、唯讀、掛載 mcp-logs 與 nginx-logs 唯讀、mcp-monitoring 網路。
網路:mcp-internal (internal: true 無外部存取)、mcp-external (nginx 對網路)、mcp-monitoring (內部)。
Nginx 反向代理配置
Nginx 配置 /nginx/conf.d/mcp-server.conf 的關鍵元素:
- 速率限制區:
limit_req_zone $binary_remote_addr zone=mcp_connect:10m rate=5r/m(連線)、mcp_call:10m rate=30r/m(工具呼叫)、limit_conn_zone zone=mcp_conn:10m(連線數) - 上游:
upstream mcp_backend { server mcp-server:8080; keepalive 16; } - 可疑 user agent 映射阻擋:
scanner、nikto、sqlmap、nmap、空值皆回 403 - TLS 配置:TLSv1.3、ECDHE 密碼套件、session timeout 1 天、會話票證關閉、OCSP stapling
- 連線限制:
limit_conn mcp_conn 10 - 安全標頭:
X-Content-Type-Options nosniff、X-Frame-Options DENY、Strict-Transport-Security max-age=63072000; includeSubDomains; preload、Content-Security-Policy default-src 'none'、Referrer-Policy no-referrer、server_tokens off - 請求主體限制:
client_max_body_size 1m、超時 10s - 路徑
/health:無認證、limit_req mcp_connect burst=10 nodelay、存取日誌關閉 - 路徑
/sse:mcp_connectburst 3、驗證Authorization非空 (否則返回 401)、proxy_buffering off、proxy_cache off、proxy_read_timeout 86400s(SSE 持久)、chunked_transfer_encoding on - 路徑
/messages/:mcp_callburst 10、驗證Authorization、驗證Content-Type為application/json(否則 415)、設定X-Request-ID、工具執行超時 60s - 其他路徑:返回 404
- 429 自訂錯誤頁:返回
{"error": "Rate limit exceeded", "retry_after": 60} - 結構化 JSON 存取日誌:
mcp_json格式含time、remote_addr、request_method、request_uri、status、body_bytes_sent、request_time、http_user_agent、request_id、ssl_client_s_dn - HTTP 轉 HTTPS:80 埠 301 重導
日誌與監控設定
結構化日誌配置
MCPStructuredFormatter 將每筆日誌格式化為 JSON,含 timestamp (ISO 8601 UTC)、level、logger、message、module、function、line。以 ContextVar 追蹤 request_id、session_id、client_identity,若有設定則附加。額外欄位如 tool_name、duration_ms、token_count、security_event 在 LogRecord 有對應屬性時附加。例外資訊以 type 與 message 形式納入。
configure_logging 設定 stdout handler 使用結構化格式,並為 mcp.security logger 增設 FileHandler 寫入 /var/log/mcp/security.json。
ToolCallAuditor 類別記錄所有工具呼叫並消毒參數:鍵名含 password、secret、token、key 時值以 ***REDACTED*** 取代;字串值超過 500 字元時截斷並加標註;記錄事件為 tool_call、工具名、安全參數、結果摘要 (前 200 字元)、持續毫秒、狀態、會話 ID、客戶端、請求 ID。
Prometheus 指標匯出器
使用 prometheus_client 定義指標:
mcp_server_info:伺服器資訊 (版本、SDK 版本、傳輸類型)tool_calls_total(Counter):標籤tool_name、statustool_call_duration(Histogram):標籤tool_name,bucket 從 0.01 至 30.0 秒tool_call_tokens(Histogram):每呼叫輸出 token 數,bucket 100 至 100000active_sessions(Gauge):活躍會話數session_duration(Histogram):會話時長,bucket 60 至 7200 秒auth_attempts_total(Counter):狀態 success/failure/expired/rate_limitedsecurity_events_total(Counter):event_type與severityinjection_attempts_total(Counter):injection_type(command/path_traversal/sql/prompt)token_spending(Counter)、cost_spending(Counter)、sampling_requests_total(Counter)
metrics_endpoint 是 Starlette 端點,回傳 generate_latest() 輸出。
網路政策配置
Kubernetes NetworkPolicy (命名空間 mcp):以 podSelector: app: mcp-server 鎖定;同時啟用 Ingress 與 Egress 政策。Ingress 僅允許 app: mcp-nginx-proxy pod 於 TCP 8080 存取,以及 app: prometheus 於 9090 抓取指標。Egress 允許至 kube-dns 的 DNS (UDP/TCP 53),允許至內部 app: postgresql 於 5432,其餘全拒 (無網際網路存取)。
iptables 等效:*filter 表預設全 DROP;允許迴環、已建立連線;INPUT 允許來自 172.18.0.0/16 的 TCP 8080 (nginx -> mcp-server);OUTPUT 允許 DNS (UDP/TCP 53)、至內部資料庫 TCP 5432;其餘全部記錄後丟棄 (prefix MCP-DROP-IN: / MCP-DROP-OUT:)。
警報規則
Prometheus 警報規則 mcp_security 群組:
MCPAuthFailureSpike:rate(mcp_auth_attempts_total{status="failure"}[5m]) > 1持續 2 分鐘,HIGH 級,認證失敗率過高MCPInjectionAttempt:increase(mcp_injection_attempts_total[5m]) > 0,CRITICAL 級,偵測到{{ $labels.injection_type }}注入MCPBudgetExceeded:mcp_cost_usd_total > 100,HIGH 級,MCP 每日花費超限MCPSamplingAbuse:rate(mcp_sampling_requests_total[5m]) > 0.5持續 1 分鐘,CRITICAL 級,可能 DoW 攻擊MCPServerDown:up{job="mcp-server"} == 0持續 1 分鐘,CRITICAL 級
參考資料
- Endor Labs:「Hardening MCP Deployments in Production」 — 企業部署樣式
- Pomerium:「Zero Trust Proxy for MCP」 — 反向代理配置指南
- OWASP ASI:代理式安全倡議 — 部署加固要求
- Docker Security Best Practices:Docker Security Documentation
- Nginx Security:Nginx Security Controls
- Prometheus Monitoring:Prometheus Best Practices
- MCP Security Guide:完整 MCP 安全總覽