Guidant AI 資安檢視總報告 · 安全需求(SR)
人、機器、外部身分提供者
「你是誰」要真的驗,不收自報。這一章 8 條:人的登入與忘記密碼、OAuth 回呼、機器(代理程式)的報到與通行證、即時連線的身分。6 條有掃描實例、2 條是標準做法。
系統有三種「誰」要驗:人(瀏覽器那頭的使用者,走密碼、LDAP、雙因子)、機器(客戶機房的代理程式,走憑證與簽章通行證)、外部身分提供者(Google、客戶的 AD/LDAP——我們不管密碼,但要確認提供者真的說了「是他」)。三種的共同原則只有一條:身分由我們簽發或向提供者驗證,不收請求裡自報的。掃描抓到的 6 條實例全是違反這一條的某種形狀——忘記密碼把鑰匙直接回給請求者、LDAP 只查人不驗密碼、Google 登入是空殼、代理程式自報編號就信。
| # | 要求 | 來源 |
|---|---|---|
| SR-01.1 | 不需登入的入口,回應不得帶可直接使用的祕密 | 掃描驗證 |
| SR-01.2 | 外部身分提供者登入或綁定,必須真的向提供者驗證 | 掃描驗證 |
| SR-01.3 | 登入失敗鎖定涵蓋所有登入路徑 | 標準做法 |
| SR-01.4 | OAuth 回呼同時驗連結與瀏覽器 | 掃描驗證 |
| SR-01.5 | 安全計數不得只存易失儲存 | 標準做法 |
| SR-01.6 | 機器身分只認簽章通行證,每請求查撤銷 | 掃描驗證 |
| SR-01.7 | 機器報到比其他端點更嚴 | 掃描驗證 |
| SR-01.8 | 即時連線每個事件重注入伺服器端身分 | 掃描驗證 |
| 欄位 | 內容 |
|---|---|
| 要求 | 不需登入就能打的端點(忘記密碼、驗證碼、邀請),回應不得帶任何可直接使用的祕密——重設鑰匙、權杖、一次性碼。祕密只能走第二通道(信箱、簡訊)送到本人。成功與失敗的回應必須逐位元組相同,不讓人靠回應差異探信箱存不存在 |
| 為什麼 | 鑰匙從回程直接交給按送出的人,等於任何知道信箱的人都能接管那個帳號——包含原廠最高權限管理員 |
| 適用連線 | C01 瀏覽器 → 前門(不需登入的入口都在這條線上暴露) |
| 對應威脅 | C01 T1 不需登入的入口把祕密從回程交出去 🔴 |
| 驗證方式 | 不登入,對忘記密碼端點送一個存在的信箱與一個不存在的信箱,兩次回應的狀態碼與內容逐位元組相同,且內容不含任何 uid/token/code 欄位 |
| 來源 | 掃描驗證:C01 D1(實例 M13-1) |
| 欄位 | 內容 |
|---|---|
| 要求 | 以外部身分提供者(Google、客戶 AD/LDAP)登入或綁定帳號時,必須真的向提供者驗證:OAuth 要驗簽、驗受眾、驗效期;LDAP 要用「查到的 DN + 使用者輸入的密碼」實際 bind 一次。不得收呼叫者自填的外部帳號編號當作已驗證 |
| 為什麼 | 「用 Google 身分」的入口若不向 Google 驗證,攻擊者填目標的 Google 編號就綁進自己帳號;LDAP 只查人不驗密碼,等於任何知道帳號的人都能登入 |
| 適用連線 | C04 瀏覽器 → api(OAuth 回呼)、C11 api → 客戶 LDAP/AD |
| 對應威脅 | C04 T3 「用 Google 身分」的入口沒真的向 Google 驗證 🟠、C11 T1 只查人、不驗密碼就放行 🟠 |
| 驗證方式 | LDAP:以正確帳號+錯誤密碼登入,必須 401 且目錄伺服器日誌看得到一次失敗的 bind。OAuth:對綁定端點送一個自填的 Google 帳號編號、不帶 id_token,必須被拒 |
| 來源 | 掃描驗證:C04 D3(實例 M13-5)、C11 D1(實例 M13-2) |
| 欄位 | 內容 |
|---|---|
| 要求 | 登入失敗次數計數與帳號鎖定(LOGIN_MAX_LOCK_COUNT/LOGIN_USER_LOCK_TIME)必須涵蓋所有登入路徑:本地密碼、LDAP/AD、雙因子驗證碼。任一路徑的失敗都要計入同一個計數 |
| 為什麼 | 只有本地路徑計數時,攻擊者拿登入頁對客戶的 AD 目錄猜密碼沒有上限——即使客戶目錄自己有鎖定政策,攻擊者借的是我們的入口 |
| 適用連線 | C11 api → 客戶 LDAP/AD、C02 前門 → api(登入端點) |
| 對應威脅 | 無掃描實例(標準做法) |
| 驗證方式 | 以 LDAP 帳號連續輸錯密碼達鎖定次數,下一次即使密碼正確也必須被鎖;雙因子驗證碼連續猜錯達上限同樣鎖 |
| 來源 | 標準做法:C11 D5◇ |
| 欄位 | 內容 |
|---|---|
| 要求 | OAuth 回呼必須同時驗兩件事:「連結是真的」——狀態碼一次性、短效、不可猜、對不上即作廢;「回來的是發起的瀏覽器」——另一個綁定值只走發起者瀏覽器的 cookie(HttpOnly、路徑限回呼、SameSite=Lax),伺服器存雜湊、定時比對 |
| 為什麼 | 只驗狀態碼時,攻擊者把自己的授權連結轉寄給別人代按,別人的雲端硬碟就接進攻擊者的公司 |
| 適用連線 | C04 瀏覽器 → api(OAuth 回呼) |
| 對應威脅 | C04 T2 授權連結轉寄給別人代按 ⚪ |
| 驗證方式 | 在瀏覽器 A 發起授權、拿到回呼網址後在瀏覽器 B 開啟(沒有 A 的 cookie),必須被拒且不換票;同一個回呼網址在 A 開第二次也必須被拒 |
| 來源 | 掃描驗證:C04 D2(實例 M24-10) |
| 欄位 | 內容 |
|---|---|
| 要求 | 「歸零就等於解除保護」的計數——登入失敗、雙因子失敗、節流——不得只存在易失儲存(Redis)。要嘛落資料庫,要嘛儲存不可用時拒絕而非放行 |
| 為什麼 | 計數只在 Redis 時,Redis 重啟(持久化刻意關掉)或拿到 Redis 帳密的人清掉 key,雙因子的猜測上限與節流當場解除 |
| 適用連線 | C06 api → Redis |
| 對應威脅 | C06 T2 拿到 Redis 帳密後歸零節流計數(無掃描實例) |
| 驗證方式 | 雙因子連續猜錯 N−1 次後重啟 Redis,再猜一次必須仍被鎖(或計數已落 DB);把 Redis 停掉,雙因子驗證與受節流的端點必須回錯誤而非放行 |
| 來源 | 標準做法:C06 D4◇ |
| 欄位 | 內容 |
|---|---|
| 要求 | 代理程式(機器)的身分只認後端簽發的簽章通行證,不認請求裡自報的機器編號。報到之外的每一支端點都要帶通行證;後端驗簽後只用證裡的機器編號;每個請求查一次撤銷/停用狀態;「回報結果」要比對「這張單是不是派給這台」。通行證效期與撤銷要配套——長效票必須搭配每請求查撤銷;撤銷後所有通道(含下載)同時失效 |
| 為什麼 | 後端只讀自報編號就相信時,站在任何連得到雲端的位置、不需帳號,照代理程式的格式打心跳說「我是客戶 X 的機器 Y」,就能領走派給 Y 的工作連同解密後的主機帳密,再替 Y 回報假結果 |
| 適用連線 | C20 agent → 主系統前門(控制面) |
| 對應威脅 | C20 T1 冒充代理程式——後端認不出對方是哪台機器 🔴 |
| 驗證方式 | 不帶通行證打心跳必須 401;帶 A 機器的通行證、body 自報 B 機器編號,後端必須以 A 處理;管理員撤銷 A 後,A 的心跳/確認/回報/下載四條通道必須立即全部 403 |
| 來源 | 掃描驗證:C20 D1(實例 M01-1)、C20 D6◇ |
| 欄位 | 內容 |
|---|---|
| 要求 | 報到(機器第一次取得身分)必須比其他端點更嚴:憑證申請檔的名稱必須等於本機指紋(擋「用自己的機器登記、名字填受害機」);帶舊編號重新登記要附舊私鑰簽的持有證明;被撤銷的機器一律拒收、不再洗回正常。註冊 token 必須有有效期與使用次數上限 |
| 為什麼 | 報到是身分的起點——報到放寬,後面每個請求驗得再嚴也是驗一個假身分。註冊 token 永不過期、不限次數時,token 一旦流出,任何人都能登記新機器進該客戶名下,直到管理員手動撤銷 |
| 適用連線 | C20 agent → 主系統前門(報到端點) |
| 對應威脅 | C20 T1 冒充代理程式 🔴 |
| 驗證方式 | 以機器 A 的指紋產憑證申請、名稱填 B,報到必須被拒;用已撤銷的機器編號重新報到必須被拒;註冊 token 超過有效期或使用次數後報到必須被拒 |
| 來源 | 掃描驗證:C20 D2(實例 M01-1) |
| 欄位 | 內容 |
|---|---|
| 要求 | 即時連線(socket)必須在交握時驗身分(JWT),每個事件重新注入伺服器端身分,例外只回原發送者。署名與租戶只取伺服器端身分——畫面送來的 user、tenant_id 只能當顯示用途,或要經過隸屬檢查。每個帶房號的事件都反查資源、確認呼叫者是該專案的人——不能只守「加入」 |
| 為什麼 | 頻道只守「加入」時,其他事件不必先加入就能指定房號,猜一個房號就進去旁聽或廣播;署名由畫面送時,紀錄會記成別人改的 |
| 適用連線 | C03 前門 → socketio |
| 對應威脅 | C03 T1 頻道不查房間歸屬,猜一個房號就進去 🟠、C03 T2 署名由畫面送上來 🟡 |
| 驗證方式 | 不帶 JWT 交握必須被拒;以專案 A 成員的身分對專案 B 的房號送 update 事件(不先 join)必須被拒;update 事件 body 帶別人的 user,落庫的署名必須是 JWT 裡的人 |
| 來源 | 掃描驗證:C03 D1(實例 M05-7、M24-9、M06-2)、C03 D2(實例 M05-9) |
來源:連線頁的防線(D);去重對照在 requirements/_drafts/D-to-SR.md。