Guidant AI 資安檢視總報告 · 安全需求(SR)

SR-01 身分驗證

人、機器、外部身分提供者

「你是誰」要真的驗,不收自報。這一章 8 條:人的登入與忘記密碼、OAuth 回呼、機器(代理程式)的報到與通行證、即時連線的身分。6 條有掃描實例、2 條是標準做法。

8 條 掃描驗證 6 標準做法 2

讀法見總表。每條六欄:要求/為什麼/適用連線/對應威脅/驗證方式/來源。現況不在這裡,看符合性矩陣。

§1

這一章管什麼

系統有三種「誰」要驗:人(瀏覽器那頭的使用者,走密碼、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 即時連線每個事件重注入伺服器端身分 掃描驗證

§2

SR-01.1 不需登入的入口,回應不得帶可直接使用的祕密

欄位 內容
要求 不需登入就能打的端點(忘記密碼、驗證碼、邀請),回應不得帶任何可直接使用的祕密——重設鑰匙、權杖、一次性碼。祕密只能走第二通道(信箱、簡訊)送到本人。成功與失敗的回應必須逐位元組相同,不讓人靠回應差異探信箱存不存在
為什麼 鑰匙從回程直接交給按送出的人,等於任何知道信箱的人都能接管那個帳號——包含原廠最高權限管理員
適用連線 C01 瀏覽器 → 前門(不需登入的入口都在這條線上暴露)
對應威脅 C01 T1 不需登入的入口把祕密從回程交出去 🔴
驗證方式 不登入,對忘記密碼端點送一個存在的信箱與一個不存在的信箱,兩次回應的狀態碼與內容逐位元組相同,且內容不含任何 uid/token/code 欄位
來源 掃描驗證:C01 D1(實例 M13-1)
§3

SR-01.2 外部身分提供者登入或綁定,必須真的向提供者驗證

欄位 內容
要求 以外部身分提供者(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)
§4

SR-01.3 登入失敗鎖定涵蓋所有登入路徑

欄位 內容
要求 登入失敗次數計數與帳號鎖定(LOGIN_MAX_LOCK_COUNT/LOGIN_USER_LOCK_TIME)必須涵蓋所有登入路徑:本地密碼、LDAP/AD、雙因子驗證碼。任一路徑的失敗都要計入同一個計數
為什麼 只有本地路徑計數時,攻擊者拿登入頁對客戶的 AD 目錄猜密碼沒有上限——即使客戶目錄自己有鎖定政策,攻擊者借的是我們的入口
適用連線 C11 api → 客戶 LDAP/AD、C02 前門 → api(登入端點)
對應威脅 無掃描實例(標準做法)
驗證方式 以 LDAP 帳號連續輸錯密碼達鎖定次數,下一次即使密碼正確也必須被鎖;雙因子驗證碼連續猜錯達上限同樣鎖
來源 標準做法:C11 D5◇
§5

SR-01.4 OAuth 回呼同時驗連結與瀏覽器

欄位 內容
要求 OAuth 回呼必須同時驗兩件事:「連結是真的」——狀態碼一次性、短效、不可猜、對不上即作廢;「回來的是發起的瀏覽器」——另一個綁定值只走發起者瀏覽器的 cookie(HttpOnly、路徑限回呼、SameSite=Lax),伺服器存雜湊、定時比對
為什麼 只驗狀態碼時,攻擊者把自己的授權連結轉寄給別人代按,別人的雲端硬碟就接進攻擊者的公司
適用連線 C04 瀏覽器 → api(OAuth 回呼)
對應威脅 C04 T2 授權連結轉寄給別人代按 ⚪
驗證方式 在瀏覽器 A 發起授權、拿到回呼網址後在瀏覽器 B 開啟(沒有 A 的 cookie),必須被拒且不換票;同一個回呼網址在 A 開第二次也必須被拒
來源 掃描驗證:C04 D2(實例 M24-10)
§6

SR-01.5 安全計數不得只存易失儲存

欄位 內容
要求 「歸零就等於解除保護」的計數——登入失敗、雙因子失敗、節流——不得只存在易失儲存(Redis)。要嘛落資料庫,要嘛儲存不可用時拒絕而非放行
為什麼 計數只在 Redis 時,Redis 重啟(持久化刻意關掉)或拿到 Redis 帳密的人清掉 key,雙因子的猜測上限與節流當場解除
適用連線 C06 api → Redis
對應威脅 C06 T2 拿到 Redis 帳密後歸零節流計數(無掃描實例)
驗證方式 雙因子連續猜錯 N−1 次後重啟 Redis,再猜一次必須仍被鎖(或計數已落 DB);把 Redis 停掉,雙因子驗證與受節流的端點必須回錯誤而非放行
來源 標準做法:C06 D4◇
§7

SR-01.6 機器身分只認簽章通行證,每請求查撤銷

欄位 內容
要求 代理程式(機器)的身分只認後端簽發的簽章通行證,不認請求裡自報的機器編號。報到之外的每一支端點都要帶通行證;後端驗簽後只用證裡的機器編號;每個請求查一次撤銷/停用狀態;「回報結果」要比對「這張單是不是派給這台」。通行證效期與撤銷要配套——長效票必須搭配每請求查撤銷;撤銷後所有通道(含下載)同時失效
為什麼 後端只讀自報編號就相信時,站在任何連得到雲端的位置、不需帳號,照代理程式的格式打心跳說「我是客戶 X 的機器 Y」,就能領走派給 Y 的工作連同解密後的主機帳密,再替 Y 回報假結果
適用連線 C20 agent → 主系統前門(控制面)
對應威脅 C20 T1 冒充代理程式——後端認不出對方是哪台機器 🔴
驗證方式 不帶通行證打心跳必須 401;帶 A 機器的通行證、body 自報 B 機器編號,後端必須以 A 處理;管理員撤銷 A 後,A 的心跳/確認/回報/下載四條通道必須立即全部 403
來源 掃描驗證:C20 D1(實例 M01-1)、C20 D6◇
§8

SR-01.7 機器報到比其他端點更嚴

欄位 內容
要求 報到(機器第一次取得身分)必須比其他端點更嚴:憑證申請檔的名稱必須等於本機指紋(擋「用自己的機器登記、名字填受害機」);帶舊編號重新登記要附舊私鑰簽的持有證明;被撤銷的機器一律拒收、不再洗回正常。註冊 token 必須有有效期與使用次數上限
為什麼 報到是身分的起點——報到放寬,後面每個請求驗得再嚴也是驗一個假身分。註冊 token 永不過期、不限次數時,token 一旦流出,任何人都能登記新機器進該客戶名下,直到管理員手動撤銷
適用連線 C20 agent → 主系統前門(報到端點)
對應威脅 C20 T1 冒充代理程式 🔴
驗證方式 以機器 A 的指紋產憑證申請、名稱填 B,報到必須被拒;用已撤銷的機器編號重新報到必須被拒;註冊 token 超過有效期或使用次數後報到必須被拒
來源 掃描驗證:C20 D2(實例 M01-1)
§9

SR-01.8 即時連線每個事件重注入伺服器端身分

欄位 內容
要求 即時連線(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。