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

SR-02 授權

租戶隔離、專案角色、能力點、總部層級

「你能不能做」要有單一判斷點,不靠每支端點自己記得查。這一章 14 條:資料庫那道隔離牆、帶編號的端點先問歸屬、能力點、總部層級、檔案歸屬、背景工作與 AI 代查的身分、跨出本系統的動作、授權停權。13 條有掃描實例、1 條是標準做法。

14 條 掃描驗證 13 標準做法 1

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

§1

這一章管什麼

授權有兩層,缺一層就破一層。第一層是資料庫牆:用資料庫的列級安全規則(RLS,Row-Level Security,資料庫依「你是誰、屬於哪家客戶」決定每一列看不看得到)把客戶之間隔開,程式層漏檢查時由它兜底(SR-02.1~02.3)。第二層是程式層守門:帶資源編號的端點要先確認「這個編號是不是你的」、功能要有能力點(RBAC,角色權限控制)、全站共用設定要總部層級(SR-02.4~02.8)。再往外是不經過畫面的路——背景工作、AI 代替使用者查詢、跨出本系統的動作、即時連線、授權停權(SR-02.9~02.14)。掃描抓到的 13 條實例有同一個形狀:守門靠每支端點、每張表、每支排程各自記得,沒有機制保證沒漏,所以本章最後兩類要求(02.13 覆蓋率清單、02.1 的全庫守衛測試)是在把「靠人記」換成「靠程式掃」。

# 要求 來源
SR-02.1 每張業務表都有資料庫隔離牆,形狀只有一種,並有全庫守衛測試 掃描驗證
SR-02.2 租戶層級只准前綴比對,「子讀母」例外只管讀 掃描驗證
SR-02.3 沒身分時隔離最嚴,繞牆必須具名宣告 掃描驗證
SR-02.4 帶資源編號的端點先 resolve 資源再查關係,守門統一走 common/authz/ 掃描驗證
SR-02.5 父子兩個編號必核對歸屬,刪實體檔用過濾後的清單 掃描驗證
SR-02.6 能力點掛在 route decorator,選單與端點守門同一張表 掃描驗證
SR-02.7 全站共用設定的寫入要總部層級,判斷點只有一個函式 掃描驗證
SR-02.8 每份檔案有歸屬登記,無人認領一律拒絕,看與刪分開問 掃描驗證
SR-02.9 背景工作入列前以呼叫者身分守門,執行時以建單者身分 掃描驗證
SR-02.10 AI 代查的每一支查詢都申報權限,未申報即拒絕 掃描驗證
SR-02.11 跨出本系統的動作之前先比對歸屬或能力點 掃描驗證
SR-02.12 HTTP 中介層擋的每一項,socket 側逐項核對有對應擋法 掃描驗證
SR-02.13 端點守門覆蓋率用程式產清單,定期對照 標準做法
SR-02.14 停權記在客戶身上,出貨版只認正式簽發站公鑰 掃描驗證

§2

SR-02.1 每張業務表都有資料庫隔離牆,形狀只有一種

欄位 內容
要求 每張業務表必須有資料庫層隔離牆(RLS),牆的形狀只有一種標準規則:表上有租戶(客戶)欄位的,掛查/增/改/刪四條規則,各自「超管或 app_tenant_allowed_for_session(tenant_id)」;沒有租戶欄位的子表必須用跟隨規則(看得到父表才看得到子表);視圖(view)必須以查詢者身分執行(security_invoker = true)。必須有一支守衛測試掃全庫,確認「有租戶欄位的表都開了牆」「開了牆的表四條規則齊全」;無牆表的程式層守門要能被看見(與 SR-02.4、SR-02.13 合用)
為什麼 牆沒蓋到時,程式層任何一支端點漏掉客戶過濾,別家客戶的專案、待辦、改善計畫、證據檔就直接出現在畫面上;沒牆的表上,漏一支守門就是別家或原廠公版範本被整份改寫、汙染之後每個用它開專案的客戶
適用連線 C05 api/socketio/worker → 資料庫
對應威脅 C05 T1 牆沒蓋到:表上有客戶欄位但隔離沒生效、或連欄位都沒有 🟠、C05 T7 沒牆的表上,程式層漏檢查就是整份改寫 🟠
驗證方式 跑一支讀 pg_class+pg_policies 的測試:列出所有有 tenant_id 欄位卻未開 RLS 的表(預期 0 張)、開了 RLS 卻缺任一條查/增/改/刪規則的表(預期 0 張)、security_invoker 不是 true 的視圖(預期 0 支)。實測:以 A 公司帳號登入,直接用 B 公司的資源編號查任一業務表,預期回空而非資料
來源 掃描驗證:C05 D1(實例 M20-1、M02-6)、C05 D7(實例 M11-2、M10-4)
§3

SR-02.2 租戶層級只准前綴比對,「子讀母」例外只管讀

欄位 內容
要求 租戶層級比對只准前綴比對(子公司路徑 /1/102/152/ 只看以自己開頭的列);不得把路徑拆成陣列逐一比對或用純文字包含比對。子公司不得看到母公司與兄弟公司;需要「子讀母」的例外(如授權)必須另加只管讀取的祖先規則,改與刪仍只吃前綴比對
為什麼 規則方向寫反時,被停權客戶或任一子公司的成員,讀得到母公司與原廠的列,而且讀得到就改得到、刪得到——刪掉停權紀錄當場復權、改母公司的掃描紀錄、拿到母公司的維運帳密
適用連線 C05 api/socketio/worker → 資料庫
對應威脅 C05 T2 牆蓋反了:子公司看得到、改得動母公司的東西 🟠
驗證方式 守衛測試讀全庫規則,斷言沒有任何規則含拆陣列寫法(如 string_to_array)或純文字包含比對;實測:以子公司(路徑 /1/102/152/)帳號對母公司(102)的授權表送 select 預期可讀、送 update/delete 預期影響 0 列,對兄弟公司與原廠的列 select 預期 0 列
來源 掃描驗證:C05 D2(實例 M18-8、M03-7)
§4

SR-02.3 沒身分時隔離最嚴,繞牆必須具名宣告

欄位 內容
要求 資料庫連線查不到登入身分時,隔離一律最嚴——看不到任何東西,不得把「沒有身分」解讀成最高權限;超管只認兩個顯式訊號(具名系統身分、原廠路徑),不得有「缺值即超管」的條件。排程與背景作業要繞牆,必須用具名的系統身分宣告(system_context("<名字>"))。刪除類背景作業(孤兒清理)刪之前必須先確認「我看得到東西」,看到 0 筆就本輪不刪
為什麼 沒身分時隔離整個關掉,那段程式碰得到的每一筆資料從一家客戶放大成全部客戶;反過來,沒身分改成最嚴後,忘了宣告身分的清理排程會「看不到任何任務」、把每一筆綁定都當孤兒,一夜之間清空所有客戶的任務與證據對應
適用連線 C05 api/socketio/worker → 資料庫、C02 前門 → api(請求期的身分來自 JWT)、C09 api → worker(背景排程)
對應威脅 C05 T3 身分沒帶上:沒身分時牆的方向整個反過來 🟠、C02 T12 身分與紀錄對不上:沒身分時隔離關掉、日誌記錯人 🟠、C09 T4 跨客戶的排程靠「找不到身分就放行」才跑得動 🟡
驗證方式 不帶任何身分變數開一條 session 查任一業務表,預期 0 列;守衛測試掃排程入口,每支排程都要有具名 system_context;把孤兒清理排程在「看不到任何任務」的狀態下執行,預期刪除筆數 0 而非全刪
來源 掃描驗證:C05 D3(實例 M04-2、M20-4)、C02 D5(實例 M04-2)、C09 D3(實例 M20-4、M06-25)
§5

SR-02.4 帶資源編號的端點先 resolve 資源再查關係,守門統一走 common/authz/

欄位 內容
要求 任何帶資源編號(專案、任務、輪次、報告、問卷等)的端點,必須先 resolve(查出)該資源,再查「呼叫者與這個資源的關係」(專案成員、角色、歸屬);守門必須統一走 common/authz/ 決策表選對的軸(platform-admin/super-admin/project-role/SSP/workflow),不得自己另寫新的守門 helper;資源域守門放在 app service 層,不放 route 層。角色集合只准放行該動作需要的角色,唯讀角色不得有寫入
為什麼 端點只驗「有沒有登入」、不驗「這個編號是不是你的」時,把網址裡的編號換成別人的(編號多半連號可猜),就讀到別人專案的稽核判定、改善計畫與證據;角色算寬時,唯讀角色能完成別人的稽核任務、對客戶機器發動掃描
適用連線 C02 前門 → api(推及:C07 檔案端點、C14 雲端硬碟舊線預覽,都是帶資源編號的端點)
對應威脅 C02 T1 換一個編號,讀到別人專案的東西 🟠、C02 T4 檢查 A、動的是 B——兩個編號不核對 🟠、C02 T5 角色算寬了:唯讀能改、建立能蓋、子公司管母公司 🟠
驗證方式 以專案 A 的成員登入,對專案 B 的專案/任務/輪次/報告編號送讀取與寫入請求,預期全部 403 或 404;以唯讀角色對寫入端點(完成任務、發動掃描、匯入範本)送請求,預期 403
來源 掃描驗證:C02 D1(實例 M02-1、M06-18、M06-7)
§6

SR-02.5 父子兩個編號必核對歸屬,刪實體檔用過濾後的清單

欄位 內容
要求 請求同時帶父子兩個編號(專案與任務、範本與子項、計畫與文件)時,守門必須驗「子屬於父」,不得只驗父;刪除時兩個編號必須核對歸屬,批次刪實體檔必須用已過濾(只含「真的屬於這張」)的清單,不得用過濾前的原清單
為什麼 只驗第一個編號時,攻擊者第一個填自己的、第二個填別人的,通過檢查後改、刪別人專案的任務與文件,甚至把別人的程序書掛到自己的控制項再下載;刪實體檔用錯清單時,別人專案或別家公司的佐證文件實體檔被銷毀、不留痕
適用連線 C02 前門 → api、C07 api → 檔案儲存
對應威脅 C02 T4 檢查 A、動的是 B——兩個編號不核對 🟠、C07 T3 檢查的是 A、刪的實體檔是 B 🟡
驗證方式 自己開專案成為管理者,刪除請求的網址填自己的計畫、文件編號填別人專案的佐證文件,預期被拒且實體檔仍在;批次刪附件,混入一個不屬於這張的檔案編號,預期該檔不被刪
來源 掃描驗證:C02 D2(實例 M06-18、M06-19)、C07 D3(實例 M11-11、M23-7)
§7

SR-02.6 能力點掛在 route decorator,選單與端點守門同一張表

欄位 內容
要求 能力點(RBAC)必須掛在 route decorator(主體域守門,只看「你是誰」);選單可見性與端點守門必須用同一張能力點表,畫面上看不到的功能,直接打網址也必須打不到(含授權過期唯讀時反灰的寫入功能)。只驗 JWT 不驗能力點的端點不得存在於有能力點對應的功能上
為什麼 選單與端點守門各管各的時,畫面沒給你的功能,從別人的操作或前端程式碼找到網址直接打就能用——拿到合規範本全文、範本掛的程序書(附檔案編號可下載)、設備清冊、流程範本,授權過期的客戶選單反灰卻照樣能寫
適用連線 C02 前門 → api(推及:C21 測試連線按鈕也屬能力點守門,見 SR-02.11)
對應威脅 C02 T3 只問登入不問能力,沒被授權的員工直接打網址 🟠、C02 T11 繞過商務授權 🟠
驗證方式 以一個沒有「合規範本」能力點的帳號,直接用該功能的端點網址(不經畫面)送 GET 與 POST,預期 403;比對選單能力點表與端點 decorator 的能力點,兩者出現的能力點集合必須一致(程式產表比對)
來源 掃描驗證:C02 D4(實例 M11-14、M18-11)
§8

SR-02.7 全站共用設定的寫入要總部層級,判斷點只有一個函式

欄位 內容
要求 全站共用設定(登入規則、帳號目錄如 LDAP、日誌轉送去向)的寫入端點必須要求總部層級;「誰算總部」的判斷只有一個函式,必須同時擋住「子公司管理員」與「別家客戶的總部管理員」,不得只擋自家子公司或根本不分層級
為什麼 守門沒分層級時,任一家客戶的管理員(或子公司管理員)就能改掉全站登入規則與 LDAP 目錄、把所有客戶的操作日誌持續轉到自己的伺服器、改暱稱在全公司每份範本埋公式
適用連線 C02 前門 → api、C12 api → SIEM(日誌轉送設定)
對應威脅 C02 T7 層級算錯:子公司改全站、他家總部管我家 🟠、C12 T3 把轉送去向改成自己的機器 🟠
驗證方式 分別以「子公司管理員」與「另一家客戶的總部管理員」對登入規則、LDAP 目錄、日誌轉送去向的寫入端點送修改請求,預期全部 403;總部管理員送同樣請求預期成功;搜尋程式碼,「是否總部」的判斷只出現在同一個函式
來源 掃描驗證:C02 D5(實例 M22-1、M09-1)、C12 D3(實例 M09-1)
§9

SR-02.8 每份檔案有歸屬登記,無人認領一律拒絕

欄位 內容
要求 每份檔案必須有歸屬登記(各業務模組登記「這檔是不是我的、這人能不能碰」);沒有任何模組認領的孤兒檔一律拒絕;拒絕時回「找不到」、不回「無權限」(不洩漏檔案存在);下載/預覽問「能不能看」、刪除問「能不能刪」,兩個問題分開問,不共用同一個判斷
為什麼 下載端點只驗登入、儲存只認 api 的金鑰時,拿到(或猜到)檔案編號就下得到別部門、別專案的稽核證據與範本附掛的程序書全文;看與刪共用判斷時,能看的人連帶能刪
適用連線 C07 api → 檔案儲存(推及:C02 的檔案下載與預覽端點)
對應威脅 C07 T2 拿到檔案編號就下載,不管那份檔是誰的 🟠、C07 T3 檢查的是 A、刪的實體檔是 B 🟡
驗證方式 以 A 部門帳號用 B 部門的檔案編號打下載與預覽端點,預期 404(不是 403);用一個沒有任何模組登記的檔案編號打下載,預期 404;以能看不能刪的角色對同一檔案送刪除,預期被拒
來源 掃描驗證:C07 D2(實例 M02-1、M11-16、M11-11)
§10

SR-02.9 背景工作入列前以呼叫者身分守門,執行時以建單者身分

欄位 內容
要求 任何「使用者按下去 → 排背景工作」的入口,必須先以呼叫者身分(受 RLS)resolve 資源、確認角色,才寫入派工單;背景必須以建單者身分執行、RLS 照常生效,只有撿單、心跳、收尾可用具名系統身分;系統身分旗標不得隨單走。跨租戶工作(如雲端硬碟同步)必須帶「預期的公司」,載入時比對專案公司與工作公司,不同則整棵不建;證據回寫前反查任務所屬專案、比對公司
為什麼 入口只驗登入、背景又以系統身分跑時,A 公司任一帳號拿 B 公司的專案編號送「重建資料夾」,就把 B 的整個專案結構建進 A 的雲端硬碟,A 再往裡丟檔,下一輪同步寫成 B 公司任務的證據——別家客戶的稽核被摻進假證據
適用連線 C09 api → worker、C14 api → Google 雲端硬碟(推及:C13 證據分類批次也是使用者按下去排背景)
對應威脅 C09 T1 入口不驗就入列,背景以系統身分替人越權 🟠、C09 T2 背景同步查資料時不帶客戶條件 🟡、C14 T2 背景工作以系統身分跑,跨公司動別人的東西 🟠
驗證方式 以 A 公司帳號對 B 公司專案編號送「重建專案資料夾」,預期入口即被拒、派工單表無新單;檢查派工單內容不含系統身分旗標;讓 A、B 兩公司接同一個 Google 帳號,A 刪一個檔觸發同步,預期 B 的任務與證據對應不變
來源 掃描驗證:C09 D1(實例 M24-2)、C09 D2(實例 M24-4)、C14 D2(實例 M24-2、M24-4)
§11

SR-02.10 AI 代查的每一支查詢都申報權限,未申報即拒絕

欄位 內容
要求 AI 代替使用者執行的每一支查詢必須申報權限(誰能呼叫、能看到什麼範圍),未申報即拒絕;提供給 AI 選擇的查詢目錄只列「這個人有權呼叫」的;查詢參數由申報處硬綁(呼叫端帶了也無效);回應序列化必須有「不送名單」(密碼鹽值、超管旗標等欄位不回)
為什麼 AI 儀表板若直接叫服務層、繞過網址入口的權限檢查,一般員工說一句「列出所有使用者與角色」就拿到組織架構、權限配置、客戶清單、未發布公告與每個帳號的密碼加密材料,而且樣本同時送到外部 AI
適用連線 C13 api → 外部 AI
對應威脅 C13 T4 AI 儀表板這條路不檢查權限,資料整包送出去 🟡
驗證方式 以沒有儀表板權限的一般員工,對 AI 儀表板說「列出所有使用者與角色」,預期查詢被拒、回應不含任何名冊;在查詢目錄新增一支未申報權限的查詢,預期啟動時或呼叫時被拒;以一般員工呼叫「專案清單」查詢,預期只回自己有權限的專案
來源 掃描驗證:C13 D4(實例 M15-2、M15-3、M17-2)
§12

SR-02.11 跨出本系統的動作之前先比對歸屬或能力點

欄位 內容
要求 任何會跨出本系統的動作——開外部問題單、改刪外部單、分享雲端資料夾、送出帳密做測試連線——必須先比對歸屬(是不是你的)或能力點(能不能改這份設定),非本人的情況一次外部呼叫都不得送出。會把已儲存帳密解密送出去的按鈕,必須掛與新增/修改/重置同一個能力點;意見回饋的改、刪、刪附件必須先 _assert_owner;雲端資料夾的分享範圍只限公司網域或專案成員
為什麼 測試連線只驗登入時,任何員工按下去就把整家公司的 SSH/WinRM 維運帳密與工具權杖解密送出(目標若由請求指定,就送進攻擊者的主機);意見回饋不比對歸屬時,任何員工改刪別人的回饋並關掉開發團隊正在處理的外部單;資料夾對全世界開放編輯時,不需要本系統帳號就能看、改、刪全公司的證據檔
適用連線 C15 api → 外部問題單系統、C14 api → Google 雲端硬碟、C21 api → agent 資料面
對應威脅 C15 T2 用別人的回饋去關開發團隊的單 🟡、C14 T3 資料夾對全世界開放編輯 🟡、C21 T2 誰都能按「測試連線」——按下去雲端就把帳密解密送出 🟠
驗證方式 以一般員工(無改設定能力點)對測試連線端點送請求,預期 403 且 agent 端無入站請求;以員工 A 對員工 B 的回饋送改與刪,預期被拒且外部問題單系統無任何呼叫(看外部呼叫日誌);取根資料夾連結,用無本系統帳號的瀏覽器開啟,預期無編輯權
來源 掃描驗證:C15 D2(實例 M23-2)、C14 D3(實例 M24-8)、C21 D2(實例 M03-2)
§13

SR-02.12 HTTP 中介層擋的每一項,socket 側逐項核對有對應擋法

欄位 內容
要求 HTTP 中介層擋的每一項——授權到期唯讀、租戶隔離、請求大小上限——即時連線(socket)側必須逐項核對有對應擋法,列表留存;socket 事件不得成為繞過 HTTP 中介層的後門
為什麼 客戶授權過期、產品轉唯讀時,網頁上所有寫入都被 HTTP 中介層擋下,但問卷答案更新走的是即時頻道、中介層根本看不到,答案照樣寫進資料庫——商務授權形同虛設
適用連線 C03 前門 → socketio
對應威脅 C03 T3 api 那邊擋了、這邊沒擋:繞過 HTTP 中介層的攔截 ⚪
驗證方式 把測試客戶的授權設成過期(唯讀),以該客戶的合法填答者透過即時頻道送問卷答案 update 事件,預期被拒、資料庫答案不變;逐項列出 HTTP 中介層清單,每項在 socket 側有對應測試
來源 掃描驗證:C03 D3(實例 M18-13)
§14

SR-02.13 端點守門覆蓋率用程式產清單,定期對照

欄位 內容
要求 端點守門覆蓋率必須用程式產清單、不得靠記憶:每支路由掛了哪一軸守門、哪些只驗 JWT;三條背景路(入列入口、執行身分、上限)同樣一份程式產的清單。清單必須定期(至少每次進版)產出並與上一版對照,新增「只驗 JWT」的路由必須有明示理由
為什麼 守門漏掉的端點不會報錯、不會被任何測試撞見,只會在攻擊者換編號時才被發現;C02 的 168 條與 C09 的入列入口都是「某一支漏了」的同一個病,沒有清單就沒人知道還剩幾支
適用連線 C02 前門 → api、C09 api → worker
對應威脅 無掃描實例(標準做法)
驗證方式 執行清單腳本,輸出每支路由 × 守門軸的表;比對上一版,新增且標「只驗 JWT」的路由逐支給出理由;列出所有會排背景的入口與其身分、上限,與清單一致
來源 標準做法:C02 D9◇、C09 D9◇
§15

SR-02.14 停權記在客戶身上,出貨版只認正式簽發站公鑰

欄位 內容
要求 停權必須記在客戶身上、不得記在授權檔上;換照、重傳舊授權檔、線上重新開通都不影響停權,解除停權只能是平台刪除那一列。出貨版必須只認正式簽發站的公鑰,開發、測試環境的公鑰不得進客戶拿到的 image
為什麼 停權記在授權檔時,被停權客戶把舊授權檔重新上傳就換成沒有停權欄位的新紀錄、唯讀當場解除;內建公鑰表若含開發環境公鑰,取得開發環境私鑰的人就能自簽全功能、永不到期的授權檔上傳到正式環境——平台最後的商務手段形同無效
適用連線 C18 api → License 簽發站
對應威脅 C18 T2 被停權後自己換一張照復權 🟠、C18 T3 用開發環境的私鑰簽出正式環境認的照 🟡
驗證方式 把客戶停權後,以該客戶帳號重新上傳舊授權檔、再走線上開通換新照,預期兩次後仍是唯讀;用開發環境私鑰簽一張授權檔上傳到出貨版,預期驗章失敗;檢查出貨 image 內建公鑰表只有正式簽發站一把
來源 掃描驗證:C18 D2(實例 M18-1)、C18 D3(實例 M18-7)

來源:連線頁的防線(D);去重對照在 requirements/_drafts/D-to-SR.md。