安全基準
概述
完整參考資料位於 references/baseline.md(章節 §0–§18,可使用 ## N. 進行 grep)。此文件為操作程序。核心原則:根據威脅等級嚴格調整,並以證據證明每個發現。 三大法則:絕不信任客戶端,絕不信任模型的程式碼,絕不信任預設值。
選擇模式
| 請求 | 模式 | 在 baseline.md 中閱讀 |
|---|---|---|
| "安全地規劃/啟動一個專案", 撰寫代理規則 | 規劃 | §0, §2, §3, §16 |
| "審查/稽核此應用程式/儲存庫/PR" | 程式碼審查 | §1.3, §4–§11, §15.2 |
| "稽核我的伺服器/自建實驗室/VPS", "它是否是最新的" | 主機稽核 | §3.3, §8.3, §12, §13 |
| "我們被駭了/洩露了密鑰" | 事件應變 | §17, §8.2 |
主機稽核包含對每個面向網際網路、持有帳戶或個人資料的應用程式進行簡短的程式碼審查。
步驟 1 — 分級(始終優先執行,並在報告中說明)
根據可觀察的事實來決定,而不是根據擁有者的稱呼:
| 等級 | 條件 | 套用章節 |
|---|---|---|
| T0 玩具 | 沒有真實使用者或資料,並且無法從網際網路存取(僅限 LAN,或位於 SSO 之後) | §2, §8 |
| T1 公開,無帳戶 | 可從網際網路存取,無登入機制,不含個人資料 | T0 + §9, §10 |
| T2 帳戶/使用者資料 | 有任何登入機制、任何個人資料(姓名、電子郵件、訪客清單)、任何儲存的 API 金鑰或令牌 | 全部 |
| T3 大規模金流/個資/受監管 | 付款、健康資料、憑證儲存庫、金融資料、超過 1000 人的資料、客戶合約 | T2 + 外部審查 + §14 |
自建實驗室主機的評級為其上運行的任何服務的最高等級。憑證匯集器(密碼管理器、n8n、Home Assistant、Portainer、docker.sock 持有者)因其爆炸半徑被視為 T3。
步驟 2 — 收集證據(唯讀)
透過 bash 執行捆綁的腳本。在遠端主機上,透過 SSH 傳輸:ssh host 'bash -s -- --remote-digests' < scripts/audit-docker-host.sh 和 ssh host 'bash -s -- <project-dir>' < scripts/scan-code.sh。
bash scripts/audit-docker-host.sh --remote-digests— OS 修補程式、重新啟動標誌、監聽中的 socket、防火牆、sshd、每個容器的使用者/權限/已發布的連接埠/docker.sock、看似密鑰的環境變數名稱、映像 freshness(UP-TO-DATE / OUTDATED / local-build)。bash scripts/scan-code.sh <project-dir>— grep 尋找秘密、注入、XSS、大量指派、客戶端提供的租戶 ID、fail-open 捕捉、弱密碼學、lockfile、Dockerfile/compose 風險、路由清單。
然後執行腳本無法完成的事項:
- 反向代理盤點:列出每個公開的主機名稱、其目標,以及身份驗證是否位於前方(NPM DB / Caddy / Traefik labels)。公開主機名稱上無 SSO/存取清單的管理後台(§3.3 清單)= 發現。
- 外部檢視:從 LAN 路徑外部,
curl -sI https://<host>檢查標頭(§10.2),並僅用 GET 探測/.env,/.git/config,/admin,/api。 - 授權追蹤:針對每個 T2+ 應用程式,為每個路由命名授權檢查及其 principal/tenant ID 的來源(§4.3)。套用 §1.3 的問題。
- 代理信任:如果應用程式讀取
X-Forwarded-For,檢查它信任哪個躍點($proxy_add_x_forwarded_for會附加;第一個值由攻擊者控制)。 - 每個角色的憑證強度:針對每個角色(管理員、員工、志工、API/MCP 令牌),找出其密鑰的設定方式,包括種子、舊版和跳過正常長度檢查的遷移路徑。使用
awk -F= '{print $1, length($2)}'檢查.env值的實際長度,絕不直接印出它們。一個弱密鑰加上一個可繞過的速率限制,算一個發現,而非兩個。 - 版本與安全公告:針對每個面向網際網路的應用程式,比較執行中的版本與當前版本以及已知的 CVE。使用網路搜尋該產品的相關安全公告。
主機閘道(§16 清單是針對專案的;以下用於主機)
- 每個公開主機名稱都已列出,且每個管理後台(§3.3)都設有 SSO、存取清單或 VPN,或者不對外公開
- Hypervisor、Docker、NPM/代理管理員、密碼庫管理員:永遠不對外公開
- 在每個不需要開放註冊的自託管應用程式(Vaultwarden、Open WebUI、Gitea、Immich 等)上已停用開放註冊
- WAN 可存取的連接埠僅為 80/443(+ VPN)。Docker 發布的連接埠會繞過 ufw:檢查路由器轉發和
DOCKER-USER - 代理傳遞的客戶端 IP 是應用程式無法被欺騙的(§4.5, X-Forwarded-For)
- docker 群組成員已列出;每個成員都具有 root 等效權限
- 密鑰檔案權限為 600;無全域可讀取的
.env/金鑰 - OS 已修補,無超過 7 天的待重新啟動;映像檔為最新(freshness 表)
- 備份近期執行過,且還原已測試過(§13.4)
參與規則
- 預設為唯讀。不寫入、不重新啟動、不更新、不執行會變更資料的 POST、不暴力破解、不掃描擁有者不擁有的主機。
- 絕不列印密碼值。僅回報變數名稱和檔案:行號。
- 檢查配置儲存(資料庫 config 表、JSON 設定、
docker inspect環境)時,僅依賴鍵和長度:jq 'paths'、SELECT key FROM …、awk -F= '{print $1, length($2)}'。絕不傾印整個配置行或檔案來查看:它會包含 API 金鑰。 - 變更(更新、chmod、配置修復)僅在報告產出後,且僅在請求者要求時執行。對於更新,請遵循下方的「安全更新」。
步驟 3 — 報告(確切格式)
- 範圍和等級:檢查的內容、等級以及一句話的理由。
- 裁決:一句話 — 可發布 / 先修復 / 停止。
- 發現,按嚴重性 Critical → Low 排序。每個發現包含:標題、
CONFIRMED(你已重現或讀取確切的程式碼/配置路徑)或LEAD(看似合理、未經證實)、證據(檔案:行號、命令+輸出片段)、一句話的影響、修復方式。 - Freshness 表(主機稽核):元件、執行中版本、最新版本、更新風險。
- 涵蓋範圍:你檢查過的 §16 閘道章節,以及你未檢查的理由。沉默不等於通過。
- 後續行動,按編號,從最小努力最高風險開始。
安全更新(「確保所有東西都是最新的」)
- 確認備份存在且是近期的(§13.4)。記錄每個容器的當前映像 ID,以便復原時可以使用
image: <repo>@<old digest>。 - 無狀態 = 應用程式沒有寫入的命名磁碟區或繫結掛載,也沒有資料庫。靜態 nginx 站台、gotenberg、flaresolverr 是無狀態的。如果不確定,將其視為有資料的。
- 分類:有資料或需要 schema 遷移的(資料庫、Immich、Paperless、Vaultwarden、Firefly、n8n、Home Assistant)→ 一次一個,先閱讀發行說明了解重大變更。無狀態(nginx 靜態站台、工具)→ 批次處理。
- 絕不透過
docker pull跳躍資料庫主版本號(例如 postgres 15→17)。這需要 dump/restore。 - 每個專案執行
docker compose pull && docker compose up -d,然後檢查健康狀態:容器狀態、錯誤日誌以及公開 URL 的 HTTP 檢查。 - 本機建置(
local-build)透過使用--pull重新建置來更新。OS 套件需要 sudo;如果你沒有權限,請回報清單和重新啟動標誌。 - 如果 pull 失敗並出現
429 toomanyrequests,請停止。要求在主機上執行docker login,或等待 6 小時後重試。不要循環。
常見錯誤
| 錯誤 | 正確 |
|---|---|
| 把所有東西都回報為 "High" | 嚴重性 = 可利用性 × 等級 × 爆炸半徑 |
| 將腳本命中視為發現 | 腳本輸出是 LEAD,直到你讀過程式碼 |
| 未經檢查就說「容器以 root 執行」 | 許多映像檔會在 entrypoint 中放棄權限;請檢查內部的 ps |
| 在 LAN 主機上說「連接埠 9000 是開放的」 | 說明誰可以存取它:僅 LAN、透過路由器轉發的 WAN,或透過代理 |
| 跳過分級,像稽核銀行一樣稽核靜態網站 | 先分級;T1 需要的是標頭和依賴項,而不是 RLS |
一次性用 :latest 更新所有東西 | 一次一個有資料的服務,並附上復原用的 digest |
透過停用控制項來修復(關閉 RLS、設定 CORS *) | 修復策略,絕不移除它(§7.2) |