Guidant AI 資安檢視總報告 · 安全測試案例(TC)
驗 SR-01 八條,共 18 個案例
每條安全需求至少一個測試案例,有對應威脅的照駭客步驟反過來打、沒有的照需求的驗證方式設計。每個案例標明用哪種測法(API/pytest/設定稽核/e2e/人工)與依矩陣的預期現況——預期過的是回歸守門,預期不過的是已知缺口,跑出來不會把已知缺口當新 bug。
SR-01 身分驗證八條:人的登入與忘記密碼、OAuth 回呼、機器(代理程式)的報到與通行證、即時連線。測法分布:守門類走 API(帶對方的 token 或不帶 token 直接打端點,一秒一條);鎖定計數走 pytest(要控制 Redis 狀態);回呼頁要兩個瀏覽器 session 走 e2e;要架假 LDAP/假 Google 的走人工。
| 案例 | 驗哪條 | 一句話 | 測法 | 預期現況 |
|---|---|---|---|---|
| TC-01.1a | SR-01.1 | 忘記密碼回應不帶鑰匙、成敗一致 | API | 預期過 |
| TC-01.1b | SR-01.1 | 全部不需登入端點的回應掃一遍不含祕密欄位 | API | 預期不過(沒有統一檢查) |
| TC-01.2a | SR-01.2 | LDAP 正確帳號+錯密碼必須 401 | 人工 | 預期過 |
| TC-01.2b | SR-01.2 | 綁定 Google 帳號不帶 id_token 必須被拒 | API | 預期過(入口已關) |
| TC-01.3a | SR-01.3 | LDAP 連續錯密碼達上限後鎖定 | 人工 | 預期不過(LDAP 失敗不計數) |
| TC-01.3b | SR-01.3 | 雙因子驗證碼連續猜錯達上限後鎖定 | API | 預期過 |
| TC-01.4a | SR-01.4 | 回呼網址在另一個瀏覽器開必須被拒 | e2e | 預期過 |
| TC-01.4b | SR-01.4 | 同一回呼網址第二次開必須被拒 | e2e | 預期過 |
| TC-01.5a | SR-01.5 | Redis 重啟後雙因子失敗計數不歸零 | pytest | 預期不過(計數只在 Redis) |
| TC-01.5b | SR-01.5 | Redis 停掉時雙因子驗證拒絕而非放行 | pytest | 預期不過(行為未盤) |
| TC-01.6a | SR-01.6 | 心跳不帶通行證必須 401 | API | 預期過 |
| TC-01.6b | SR-01.6 | 帶 A 的通行證、自報 B 的編號,後端以 A 處理 | API | 預期過 |
| TC-01.6c | SR-01.6 | 撤銷 A 後四條通道立即全部 403 | API | 預期過 |
| TC-01.7a | SR-01.7 | 憑證申請名稱填別台的指紋,報到必須被拒 | 人工 | 預期過 |
| TC-01.7b | SR-01.7 | 註冊 token 超過有效期或次數後報到必須被拒 | API | 預期不過(token 無效期無次數) |
| TC-01.8a | SR-01.8 | 不帶 JWT 交握必須被拒 | API | 預期過 |
| TC-01.8b | SR-01.8 | 專案 A 成員對專案 B 房號送 update(不先 join)必須被拒 | API | 預期過 |
| TC-01.8c | SR-01.8 | update 帶別人的 user,落庫署名必須是 JWT 裡的人 | API | 預期過 |
| 欄位 | 內容 |
|---|---|
| 驗哪條 | SR-01.1 不需登入的入口,回應不得帶可直接使用的祕密 |
| 對應威脅 | C01 T1 不需登入的入口把祕密從回程交出去 🔴(實例 M13-1) |
| 測法 | API——不開瀏覽器,直接對 POST /api/1.0/forget-password 送請求 |
| 前置 | DEV 環境;一個已存在的使用者信箱 exists@…;一個不存在的 nobody@…;遮罩設定(FORGET_PASSWORD_MASK)維持預設開 |
| 步驟 | ① 不帶任何登入資訊,送 {"email": "exists@…"};② 記下狀態碼與完整回應 body;③ 同樣送 {"email": "nobody@…"};④ 比對兩次 |
| 預期結果 | 兩次狀態碼相同、body 逐位元組相同;body 不含 uid、token、code、request_uid 任何一個 key;回應時間差 < 100ms(不讓時間差洩漏信箱存不存在) |
| 預期現況 | 預期過——連線頁 C01 D1:回應已改固定空白、成功失敗逐位元組相同(CM-1575) |
| 自動化落點 | test repo site-regression/api/(Playwright request context,不開瀏覽器) |
| 欄位 | 內容 |
|---|---|
| 驗哪條 | SR-01.1 |
| 對應威脅 | 同上 🔴——這個案例驗的是「通則」,不是單一端點 |
| 測法 | API+設定稽核——先用程式列出所有不掛身分檢查的路由(SR-02.13 要的清單),再逐一打 |
| 前置 | 路由清單腳本(collect_routes.py 加「掛哪個守門」欄,目前沒有);每支端點準備一組合法輸入 |
| 步驟 | ① 產「只驗 JWT 或完全不驗」的路由清單;② 對每支送合法請求;③ 回應 body 遞迴掃 key 名含 token/secret/password/key/uid(排除資源本身的 uid) |
| 預期結果 | 零命中 |
| 預期現況 | 預期不過(已知缺口)——C01 D1 ⚠️:沒有統一的「不需登入端點回應檢查」,靠逐支 review;清單腳本也還沒有 |
| 自動化落點 | 清單腳本進 scripts/deliverables/;掃描進 test repo API 層 |
| 欄位 | 內容 |
|---|---|
| 驗哪條 | SR-01.2 外部身分提供者登入必須真的向提供者驗證 |
| 對應威脅 | C11 T1 只查人、不驗密碼就放行 🟠(實例 M13-2) |
| 測法 | 人工——要一台真的 LDAP/AD(DEV 有 192.168.50.160),且要看目錄那端的日誌 |
| 前置 | 系統設定 → 登入方式設 LDAP,指向測試 AD;一個 AD 上存在的帳號 |
| 步驟 | ① 登入頁輸入該帳號+錯誤密碼;② 看回應;③ 到 AD 伺服器看安全日誌(事件 4625 登入失敗) |
| 預期結果 | ② 401;③ AD 日誌有一筆該帳號的失敗 bind——證明密碼真的送去驗了,不是只查到人就放行 |
| 預期現況 | 預期過——C11 D1 ✅ 兩段式,DEV 真實 AD 手測三態 |
| 自動化落點 | 人工(可轉 API 層但要 mock LDAP,先不做) |
| 欄位 | 內容 |
|---|---|
| 驗哪條 | SR-01.2 |
| 對應威脅 | C04 T3 「用 Google 身分」的入口沒真的向 Google 驗證 🟠(實例 M13-5) |
| 測法 | API |
| 前置 | 一般使用者的 JWT |
| 步驟 | ① 帶 JWT 對綁定外部帳號端點送 {"provider": "google", "external_id": "任意字串"},不帶 id_token;② 看回應;③ 查該使用者的綁定紀錄 |
| 預期結果 | ② 400 或 405(入口已關);③ 綁定紀錄沒有新增 |
| 預期現況 | 預期過——C04 D3:決策者裁定關閉入口,登入方式白名單不含 Google。⚠️ 但是「關掉」不是「修好」,程式還在;若日後重開要先補驗證,這個案例會變成守門 |
| 自動化落點 | test repo API 層 |
| 欄位 | 內容 |
|---|---|
| 驗哪條 | SR-01.3 登入失敗鎖定涵蓋所有登入路徑 |
| 對應威脅 | 無掃描實例(標準做法) |
| 測法 | 人工(要真 LDAP);之後可轉 pytest mock adapter |
| 前置 | 登入方式 LDAP;LOGIN_MAX_LOCK_COUNT(預設 5);一個 AD 帳號 |
| 步驟 | ① 用該帳號連續輸錯密碼 5 次;② 第 6 次輸入正確密碼 |
| 預期結果 | ② 必須被拒(帳號已鎖),回應與本地帳號鎖定時相同 |
| 預期現況 | 預期不過(已知缺口)——C11 D5◇ ⚠️:失敗計數只在 is_password_correct(),LDAP adapter 第二段 bind 失敗直接拋 401 不碰 login_failed_count。第 6 次正確密碼會直接登入成功 |
| 自動化落點 | 修了之後轉 pytest(mock LDAP adapter 回 bind 失敗,斷言 login_failed_count 遞增) |
| 欄位 | 內容 |
|---|---|
| 驗哪條 | SR-01.4 OAuth 回呼同時驗連結與瀏覽器 |
| 對應威脅 | C04 T2 授權連結轉寄給別人代按 ⚪(實例 M24-10) |
| 測法 | e2e——要兩個獨立的瀏覽器 context(不同 cookie jar) |
| 前置 | Drive 整合已設定;管理員帳號;Playwright 兩個 context A、B |
| 步驟 | ① A 登入,系統設定 → 雲端硬碟 → 連結,攔截導向 Google 前的授權網址,記下 state;② 不真的去 Google,直接組回呼網址 …/callback?code=假&state=<記下的>;③ 在 B(沒有 A 的 cookie)開這個網址;④ 查 Drive 綁定紀錄 |
| 預期結果 | ③ 回呼頁顯示錯誤代碼(GRC_400134 類)、不換票;④ 沒有新增綁定 |
| 預期現況 | 預期過——C04 D2 ✅ browser_nonce cookie 綁定,缺 cookie 一律拒 |
| 自動化落點 | test repo site-regression/(Cucumber + Playwright,多 context) |
| 欄位 | 內容 |
|---|---|
| 驗哪條 | SR-01.4 |
| 對應威脅 | 同上 |
| 測法 | e2e |
| 前置 | 同 TC-01.4a |
| 步驟 | ① 在 A 完成一次正常回呼(或走 TC-01.4a 的假 code 讓它失敗一次);② 在 A 再開同一個回呼網址 |
| 預期結果 | ② 被拒——state 一次性,用過即刪 |
| 預期現況 | 預期過——C04 D2 ✅ handle_callback() 先刪 state 再比對 |
| 自動化落點 | 同上,與 TC-01.4a 同一個 feature |
| 欄位 | 內容 |
|---|---|
| 驗哪條 | SR-01.5 安全計數不得只存易失儲存 |
| 對應威脅 | C06 T2 拿到 Redis 帳密後歸零節流計數(無掃描實例) |
| 測法 | pytest——要控制 Redis 狀態,進程式裡測比較穩 |
| 前置 | 測試用 Redis;jedi_iam.mfa 的失敗計數服務 |
| 步驟 | ① 對帳號 X 記 N−1 次雙因子失敗;② FLUSHALL(模擬重啟,持久化本來就關);③ 再記一次失敗,查是否達鎖定 |
| 預期結果 | ③ 達鎖定(計數在 DB,或 Redis 有持久化) |
| 預期現況 | 預期不過(已知缺口)——C06 D4◇ ⚠️:計數只在 Redis,Redis 重啟即歸零 |
| 自動化落點 | BE repo test/(套件層的在套件 repo) |
| 欄位 | 內容 |
|---|---|
| 驗哪條 | SR-01.5 |
| 對應威脅 | 同上 |
| 測法 | pytest |
| 前置 | 同上 |
| 步驟 | ① 把 Redis 連線設成不可用(錯的 host);② 呼叫雙因子驗證(送正確碼) |
| 預期結果 | ② 拋錯誤、拒絕登入——不能因為計數讀不到就當成零次失敗放行 |
| 預期現況 | 預期不過(已知缺口)——C06 D4◇ ⚠️:「Redis 連不上時的行為沒有盤」,各呼叫端自己決定。跑出來才知道是拒絕還是放行;放行就是缺口 |
| 自動化落點 | 同上 |
| 欄位 | 內容 |
|---|---|
| 驗哪條 | SR-01.6 機器身分只認簽章通行證 |
| 對應威脅 | C20 T1 冒充代理程式——後端認不出對方是哪台機器 🔴(實例 M01-1) |
| 測法 | API——照 T1 的步驟 ①②:不需帳號,照代理程式格式打心跳 |
| 前置 | 一台已註冊的 agent 的機器編號(從管理畫面抄) |
| 步驟 | ① 不帶 Authorization,對 POST /api/1.0/agents/heartbeat 送 {"agent_uid": "<那台的編號>"} |
| 預期結果 | 401;回應不含任何派工內容 |
| 預期現況 | 預期過——C20 D1 ✅ agent_pass_required 守門 |
| 自動化落點 | test repo API 層 |
| 欄位 | 內容 |
|---|---|
| 驗哪條 | SR-01.6 |
| 對應威脅 | 同上 🔴——T1 步驟 ③「後端若只讀自報編號就相信」 |
| 測法 | API |
| 前置 | 兩台已註冊的 agent A、B;A 的通行證;B 名下有一張待領的派工單、A 名下沒有 |
| 步驟 | ① 帶 A 的通行證打心跳,body 寫 {"agent_uid": "<B 的編號>"};② 看回應裡有沒有派工單;③ 查 B 那張單的狀態 |
| 預期結果 | ② 空(A 名下沒單)或 403;③ B 的單仍是待領,沒被 A 領走 |
| 預期現況 | 預期過——C20 D1 ✅ 驗簽後只認 sub |
| 自動化落點 | test repo API 層 |
| 欄位 | 內容 |
|---|---|
| 驗哪條 | SR-01.6 |
| 對應威脅 | 同上 🔴——T1 步驟 ⑤「撤銷後若通道不查撤銷」 |
| 測法 | API |
| 前置 | 一台已註冊的 agent A 與其通行證;一張派給 A 的單 |
| 步驟 | ① 用 A 的通行證各打一次心跳、確認收到、回報結果、下載檔案,記下都成功;② 管理畫面撤銷 A;③ 不等待,立刻用同一張通行證再打四條 |
| 預期結果 | ③ 四條全部 403(不是 401——通行證驗簽仍過,是撤銷查表擋的) |
| 預期現況 | 預期過——C20 D1 ✅ 每請求查 remote_agents;DEV 實測撤銷後四通道全 403 |
| 自動化落點 | test repo API 層 |
| 欄位 | 內容 |
|---|---|
| 驗哪條 | SR-01.7 機器報到比其他端點更嚴 |
| 對應威脅 | C20 T1 冒充代理程式 🔴 |
| 測法 | 人工——要自己產憑證申請檔(CSR),步驟多 |
| 前置 | 有效的註冊 token;兩台機器 A、B 的硬體指紋;openssl |
| 步驟 | ① 在 A 上產私鑰與 CSR,但 CN 填 B 的指紋;② 帶 token 對 POST /api/1.0/agents/register 送這份 CSR 與 A 的指紋 |
| 預期結果 | 被拒——CSR 名稱必須等於本機指紋 |
| 預期現況 | 預期過——C20 D2 ✅ agent_enrollment_service.py 三件都有 |
| 自動化落點 | 人工;可轉 pytest(直接呼叫 enrollment service) |
| 欄位 | 內容 |
|---|---|
| 驗哪條 | SR-01.7 |
| 對應威脅 | 同上 🔴 |
| 測法 | API |
| 前置 | 一組註冊 token |
| 步驟 | ① 用同一組 token 報到兩台不同的機器;② 查 token 有沒有「有效期」「剩餘次數」欄位 |
| 預期結果 | ① 第二台應被拒(或 token 有次數上限且用完);② 欄位存在 |
| 預期現況 | 預期不過(已知缺口)——C20 D2 ⚠️:enroll_token 實體只有 enabled 旗標,無過期無次數。第二台會成功 |
| 自動化落點 | test repo API 層(修了之後變守門) |
| 欄位 | 內容 |
|---|---|
| 驗哪條 | SR-01.8 即時連線每個事件重注入伺服器端身分 |
| 對應威脅 | C03 T1 頻道不查房間歸屬 🟠(實例 M24-9) |
| 測法 | API——socket.io client 不開瀏覽器 |
| 前置 | socketio 端點 /socket.io,namespace /socket/fill-survey |
| 步驟 | ① 不帶 auth.token 連 namespace |
| 預期結果 | 交握被拒(connect_error),不進入任何房間 |
| 預期現況 | 預期過——C03 D1 ✅ 繼承 AuthenticatedNamespace |
| 自動化落點 | test repo API 層(socket.io-client) |
| 欄位 | 內容 |
|---|---|
| 驗哪條 | SR-01.8 |
| 對應威脅 | 同上 🟠——T1 步驟「猜一個房號就進去」 |
| 測法 | API |
| 前置 | 使用者 U 是專案 A 成員、不是 B 成員;B 有一份問卷,其房號(task uid)已知 |
| 步驟 | ① 帶 U 的 JWT 連 namespace;② 不送 join,直接送 update 事件、房號填 B 的 |
| 預期結果 | 事件被拒、只回給 U 自己錯誤;B 的問卷沒有變更;B 房間裡其他人沒收到廣播 |
| 預期現況 | 預期過——C03 D1 ✅ 六個事件全部 _assert_room_member |
| 自動化落點 | test repo API 層 |
| 欄位 | 內容 |
|---|---|
| 驗哪條 | SR-01.8 |
| 對應威脅 | C03 T2 署名由畫面送上來 🟡(實例 M05-9) |
| 測法 | API |
| 前置 | U 是專案 A 成員;A 的問卷房號;另一個使用者 V 的 login_name |
| 步驟 | ① 帶 U 的 JWT 連線、join A 的房號;② 送 update,body 的 user 欄填 V;③ 查該筆答案的 updated_user |
| 預期結果 | ③ 是 U 不是 V |
| 預期現況 | 預期過——C03 D2 ✅ on_update 署名取 get_user_context().login_name。⚠️ 但 join/leave/edit/endedit 四個事件的廣播 user 仍取畫面值——那是顯示不是落庫,另開案例驗廣播內容時再補 |
| 自動化落點 | test repo API 層 |
案例從 SR-01 的驗證方式與對應威脅的「駭客怎麼打」反推;預期現況取自符合性矩陣(2026-10-02)。測試實跑後結果回填矩陣。