Guidant AI 資安檢視總報告 · 連線清單
唯一一條由我們雲端主動連進客戶機房的線。「測試連線」按鈕、取消掃描、取回證據檔都走它,而且請求裡會帶解密後的工具帳密要代理程式拿去連內網。這一頁回答:這條線怎麼連、哪些功能走它、它帶來什麼威脅、駭客怎麼打、我們要怎麼防、目前做到哪。
這一頁怎麼來的:DFD Level 0 把產品運作時的連線編成 C01~C25;STRIDE 六頁每一條問題都標了發生在哪幾條線。這裡以連線為單位整理。個別問題修了沒不在這頁講,請點條目連回 STRIDE 頁。
%%{init: {'theme':'base','themeVariables':{'primaryColor':'#E2F0F1','primaryTextColor':'#14201F','primaryBorderColor':'#0E7C86','secondaryColor':'#EEF2F3','secondaryTextColor':'#14201F','tertiaryColor':'#FBFCFC','tertiaryTextColor':'#14201F','lineColor':'#4A5A5C','textColor':'#14201F','mainBkg':'#E2F0F1','nodeBorder':'#0E7C86','nodeTextColor':'#14201F','edgeLabelBackground':'#FBFCFC','titleColor':'#14201F','clusterBkg':'#FBFCFC','clusterBorder':'#E4EAEB'}}}%%
flowchart LR
U(["👤 有工具設定修改權的使用者<br/>按「測試連線」"])
subgraph MAIN["🏠 主系統 stack"]
API["guidant-api<br/>解密工具帳密<br/>簽 60 秒 JWT<br/>出示雲端客戶端憑證"]
end
subgraph AGENTBOX["🏢 客戶機房"]
direction TB
subgraph AG["evidence-agent :8443(agent 自起 TLS)"]
direction TB
MTLS["① 要求客戶端憑證<br/>(CERT_REQUIRED)"]
JWTG["② 驗 JWT:簽章・發行者・<br/>aud=本機編號・bound_fp=本機指紋・60 秒"]
EP["/detection/probe 測試連線<br/>/detection/cancel 取消<br/>/blob/… 取檔"]
OPEN["/health・/agent-info<br/>不驗"]
MTLS --> JWTG --> EP
MTLS --> OPEN
end
TOOL(["🖥 內網:受檢主機/工具伺服器"])
EP -- "C23/C23a · 用請求帶的帳密去連" --> TOOL
end
U -- "C02" --> API
API == "C21 · HTTPS :8443 · mTLS+JWT<br/>body 含明文工具帳密" ==> MTLS
classDef ext fill:#FBFCFC,stroke:#9AA8AA,stroke-dasharray:3 2
classDef open fill:#FDECEC,stroke:#C0392B
class U,TOOL ext
class OPEN open
| 項目 | 現況 |
|---|---|
| 誰 → 誰 | 我們的 guidant-api → 客戶機房 evidence-agent 容器的 :8443。方向是雲端連入客戶,客戶防火牆要放行 8443 入站;agent 的對外位址在安裝時填(AGENT_BASE_URL),報到時回報給雲端存在 remote_agents.base_url |
| 協定/埠 | HTTPS :8443。TLS 由 agent 程式自己承接(nginx sidecar 已退役),最低 TLS 1.2。認證模式下雲端要求 base_url 必須是 https://,否則不發 |
| 傳什麼 | 三件事:① 測試連線(POST /detection/probe)——body 帶解密後的工具帳密(SSH/WinRM 密碼、OpenVAS/ZAP/SonarQube 帳密或權杖)與目標主機清單,要 agent 就近去連並回成敗;② 取消掃描(POST /detection/cancel)——帶任務編號;③ 取檔(GET /blob/<uid>、/blob/<uid>/sha256、/blob/<uid>/pdf)——雲端從 agent 暫存取回證據檔 |
| 怎麼驗身分 | 兩層,真 mTLS:① agent 的 TLS 伺服器設 verify_mode = CERT_REQUIRED,雲端必須出示由同一個內部 CA 簽的客戶端憑證(BE .env 的 AGENT_CLOUD_CLIENT_CERT/KEY),沒帶直接 400;② 通過 TLS 後,before_request 驗 Authorization: Bearer <JWT>——RS256 簽章、發行者固定、受眾必須等於這台 agent 的編號、bound_fp 必須等於這台的硬體指紋(product_uuid+machine-id 算出)、效期 60 秒(AGENT_JWT_TTL_SEC,每次請求現簽) |
| 哪些不驗 | /health(容器健康檢查用、綁 :8000 僅容器內)與 /agent-info 不在受保護前綴內,:8443 上也打得到。/agent-info 回什麼要注意(見六) |
| 雲端怎麼信 agent | 雲端用內部 CA(AGENT_CA_CERT)驗 agent 出示的伺服器憑證——那張是 agent 報到時雲端自己簽的,SAN 取安裝時填的對外位址 |
| 帳密在這條線上的樣子 | probe 的 body 是明文帳密(TLS 保護傳輸)。agent 端只在本次請求記憶體內使用、不落地、log 只記主機與工具不記帳密 |
| 何時存在 | 客戶有裝代理程式、且雲端 AGENT_AUTH_MODE=full(installer 預設寫 full)。mode=none 是 demo 模式:裸 http、不驗任何東西——正式部署不該出現 |
依據:agent repo core/data_plane_server.py(CERT_REQUIRED)、core/data_plane_auth.py(受保護前綴 /blob、/detection;JWT 四項檢查)、core/agent_auth.py(verify_data_plane_jwt)、api/detection/routes/detection_route.py、api/blob/__init__.py;套件 jedi_remote_agent/common/agent_auth/tls.py(雲端 mTLS context)、jwt_util.py(jwt_ttl_sec=60)、jedi_detection/infra/detection_tools/connector/agent_probe_client.py;主專案 config/config.py:274-284、scripts/installer/install.sh:1705(AGENT_AUTH_MODE=full)。
| 群 | 功能 | 對威脅的意義 |
|---|---|---|
| ① 測試連線 | 檢測工具設定頁的「測試連線」按鈕:雲端把該租戶的工具帳密解密、連同目標主機清單送給 agent,agent 用既有 connector 去連並回「成功/失敗+訊息」 | 這是唯一一條會把解密後的客戶帳密主動送出雲端的路徑(掃描本身是 agent 來領、見 C20)。誰能按、按下去連到哪、結果回什麼,三件事都是攻擊面 |
| ② 取消掃描 | 使用者按取消,雲端通知 agent 真的中斷正在跑的子行程 | 影響的是可用性與紀錄完整性,不帶祕密 |
| ③ 取回證據檔 | 掃描報告先存在 agent 旁的 SeaweedFS(C22),雲端經這條線取回寫進主系統儲存 | 取回的檔案會進證據清單、被人預覽——內容可信度在 C20 T2 討論 |
掃描在這條線上抓到 4 條問題(M01-1、M03-2、M03-14、M03-6),歸納起來是四種攻擊手法。這條線特別的地方:它的入口在雲端那頭的畫面——三條(T2~T4)都是先在畫面上按一顆按鈕,雲端替攻擊者把帳密與請求送進客戶內網。
| # | 風險 | 威脅 | 駭客怎麼打 | 得手什麼 | STRIDE | 實例 |
|---|---|---|---|---|---|---|
| T1 | 🔴 最嚴重 | 冒充雲端打進客戶機房 | ① 連得到客戶的 :8443(同一內網、或客戶把 8443 開到了公網);② 若 agent 不要求客戶端憑證、或不驗 JWT 的受眾與指紋,就能照格式送一個 /detection/probe,帶自己編的「帳密」與目標——讓 agent 替他去敲內網任何主機;③ 或送 /blob/<uid> 把 agent 暫存的證據檔取走 |
把客戶內網的代理程式當跳板掃內網;取走掃描報告 | S 冒充身分、I 資料外洩 | M01-1 代理程式五條通訊管道全部不檢查對方是誰 🔴(該條的「下載證據檔」通道在這條線上) |
| T2 | 🟠 高 | 誰都能按「測試連線」——按下去雲端就把帳密解密送出 | ① 公司裡任何能登入的帳號(不需管理權限);② 打開檢測工具設定頁記下某份設定的編號;③ 直接送測試連線請求,端點若只驗登入不驗「你能不能改這份設定」,就把 SSH/WinRM 密碼與三套工具權杖解密、經這條線送到 agent;④ 目標主機若由請求指定(T3),帳密就送進攻擊者控制的機器 | 整家公司用來掃描主機的維運帳密與工具權杖 | E 權限提升、I 資料外洩 | M03-2 「測試連線」沒檢查權限,按下去就把維運帳密解密送到指定主機 🟠 |
| T3 | 🟡 中 | 測試連線連到哪,由按的人自己填——代理程式變成內網探測器 | ① 有權按測試連線的人;② 目標清單放一整段內網位址;③ agent 逐台去連;④ 從每台「連得上/被拒/逾時」的差異畫出客戶內網哪些機器開著哪些服務。帳密也隨之送到每一台 | 客戶內網的主機存活與服務分布圖;帳密被送到非預期主機 | I 資料外洩 | M03-6 「測試連線」要連到哪台主機由呼叫者自己指定 🟡 |
| T4 | 🟡 中 | 不該動手的人取消或干擾正在跑的掃描 | ① 以唯讀角色登入;② 對別人的掃描按取消,雲端經這條線叫 agent 真的中斷子行程;③ 反覆取消再重派,正常檢測永遠跑不完 | 干擾稽核檢測、改掉執行紀錄 | E 權限提升、D 讓服務停擺 | M03-14 唯讀角色可以對客戶機器發動掃描、刪掉執行紀錄 🟡(取消那一支走這條線) |
四種手法的共同點:T1 是這條線自己的門——客戶機房對外開了一個埠,認錯對方就等於內網被接管。T2~T4 的門其實在 C02——這條線只是忠實地把雲端叫它做的事做掉;但因為它做的事是「拿客戶的鑰匙去開客戶的門」,上游守門一鬆,後果直接落在客戶內網。
對應四種威脅與這條線的本質,防線分七條。D1~D4 直接對應掃描抓到的四種手法;標 ◇ 的(D5~D7)是依這條線的特性補的標準防線,掃描範圍沒涵蓋、沒有實例。「目前」欄寫程式與部署裡實際有的機制;⚠️ 表示目前只靠慣例或只守到局部。
| # | 防線 | 擋哪種威脅 | 目前做到哪 |
|---|---|---|---|
| D1 | 入站兩層驗:先要憑證、再驗綁定這台的短效票。TLS 層強制客戶端憑證;JWT 要綁受眾(這台編號)與硬體指紋、效期以秒計、每次現簽 | T1 | ✅ build_server_ssl_context 設 CERT_REQUIRED(docstring 特別標紅:預設 CERT_NONE 會讓 CA 載入看似成功但完全不要求憑證);✅ JWT 驗簽章+iss+aud=agent_uid+bound_fp=device_uuid+exp,60 秒;✅ 每次請求讀當下的註冊狀態(重註冊換 uid 後立刻用新 uid 驗)。⚠️ /health、/agent-info 在 :8443 上不驗 |
| D2 | 會送帳密出去的按鈕,要先問「你能不能改這份設定」。測試連線掛與新增/修改/重置相同的能力點守門 | T2 | ✅ 路由補上 @capability_required(plugin_update_capability),寫法照同檔三處既有宣告。⚠️ 這是唯一的防線——「連到哪裡」刻意不設白名單(D3) |
| D3 | 目標由呼叫者填,就要限量、收斂回應、記下誰按的:單次主機數上限;回應只回成功/失敗,逐台診斷進日誌;稽核日誌記操作者、客戶、設定、工具、代理程式、目標清單 | T3 | ✅ 三件都做了:DETECTION_TEST_CONNECTION_MAX_HOSTS 預設 32、回應收斂、稽核日誌。裁定不設目標白名單(掃描目標是客戶自己的主機,哪台算合法由客戶管)。⚠️ 所以「有權按的人把帳密送到自己機器」這條路依設計是開的——防線是 D2 的權限門,不是目標限制 |
| D4 | 取消與重派只放行任務被指派人與專案管理者 | T4 | ✅ 八支寫入端點換成 assert_job_operator(見 C02 D1) |
| D5 ◇ | agent 端對 probe 請求也要有上限:單次主機數、逾時、併發數——雲端的限制是一道,agent 自己再守一道,不信雲端一定對 | T3 的延伸、T1 | ⚠️ agent 端 DetectionProbeRoute 沒有自己的主機數上限,照雲端送來的清單逐台連;逾時由各 connector 自訂(OpenVAS 60 秒等)。雲端那層守住就沒事,但這條線的設計哲學是「不信對方」,agent 端應該也量一次 |
| D6 ◇ | 8443 只對雲端開:客戶防火牆只放行雲端的來源 IP;安裝文件要明寫 | T1 | ⚠️ 安裝手冊寫「雲端連得到本機 :8443(防火牆要放行 8443)」,沒說只放行雲端來源。mTLS 擋得住冒充,但開到公網就是多一個可被探測、可被打 TLS 層漏洞的埠 |
| D7 ◇ | demo 模式不得出現在正式部署:AGENT_AUTH_MODE=none 是裸 http、不驗任何東西 |
T1 | ✅ installer 寫 AGENT_AUTH_MODE=full、agent compose 預設 full、雲端在 full 模式下拒絕非 https 的 base_url;維運工具 guidantai 在 mode 不對時明確報錯指路。⚠️ 主專案 config.py 預設值仍是 none——靠 installer 寫 .env 蓋掉;手動部署忘了設就是 demo 模式,且沒有啟動期檢查擋它 |
最該補的一步:D7 的預設值。AGENT_AUTH_MODE 在程式裡預設 none、靠 installer 寫成 full;正式設定類(StagingPremiseConfig 等)應該在啟動時檢查 mode != full 就拒絕啟動——同 SANDBOX_MODE 留空在正式設定會拒絕啟動的做法。一行檢查,擋掉「手動部署忘了設、整條線變裸 http」。
/agent-info 在 :8443 回了什麼(D1):不驗身分就能打,回應裡若有版本、指紋、編號,等於幫攻擊者對 T1 做前置偵察。沒看。AGENT_AUTH_MODE 的啟動期檢查(D7):正式設定類沒擋 none。依據:STRIDE 六頁信任邊界連線標記(CM-2403/2404 驗收後版本)、agent repo core/data_plane_server.py/core/data_plane_auth.py/core/agent_auth.py/api/detection/routes/detection_route.py/api/blob/__init__.py/deploy/README.md/deploy/docker-compose.yml、套件 jedi_remote_agent/common/agent_auth/tls.py/jwt_util.py/settings.py、jedi_detection/infra/detection_tools/connector/agent_probe_client.py、主專案 config/config.py/scripts/installer/install.sh/scripts/installer/guidantai、DFD Level 0。