---
title: SR-02 授權
eyebrow: Guidant AI 資安檢視總報告 · 安全需求（SR）
h1: SR-02 授權
subtitle: 租戶隔離、專案角色、能力點、總部層級
lede: 「你能不能做」要有單一判斷點，不靠每支端點自己記得查。這一章 **14 條**：資料庫那道隔離牆、帶編號的端點先問歸屬、能力點、總部層級、檔案歸屬、背景工作與 AI 代查的身分、跨出本系統的動作、授權停權。13 條有掃描實例、1 條是標準做法。
chips:
  - { text: "14 條", kind: accent }
  - { text: "掃描驗證 13", kind: ok }
  - { text: "標準做法 1", kind: plain }
---

> 讀法見[總表](SR-00-overview.html)。每條六欄：要求／為什麼／適用連線／對應威脅／驗證方式／來源。現況不在這裡，看[符合性矩陣](SR-matrix.html)。

## 這一章管什麼

授權有兩層，缺一層就破一層。**第一層是資料庫牆**：用資料庫的列級安全規則（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 | 停權記在客戶身上，出貨版只認正式簽發站公鑰 | 掃描驗證 |

---

## SR-02.1 每張業務表都有資料庫隔離牆，形狀只有一種 {#sr-02-1}

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

## SR-02.2 租戶層級只准前綴比對，「子讀母」例外只管讀 {#sr-02-2}

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

## SR-02.3 沒身分時隔離最嚴，繞牆必須具名宣告 {#sr-02-3}

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

## SR-02.4 帶資源編號的端點先 resolve 資源再查關係，守門統一走 `common/authz/` {#sr-02-4}

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

## SR-02.5 父子兩個編號必核對歸屬，刪實體檔用過濾後的清單 {#sr-02-5}

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

## SR-02.6 能力點掛在 route decorator，選單與端點守門同一張表 {#sr-02-6}

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

## SR-02.7 全站共用設定的寫入要總部層級，判斷點只有一個函式 {#sr-02-7}

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

## SR-02.8 每份檔案有歸屬登記，無人認領一律拒絕 {#sr-02-8}

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

## SR-02.9 背景工作入列前以呼叫者身分守門，執行時以建單者身分 {#sr-02-9}

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

## SR-02.10 AI 代查的每一支查詢都申報權限，未申報即拒絕 {#sr-02-10}

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

## SR-02.11 跨出本系統的動作之前先比對歸屬或能力點 {#sr-02-11}

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

## SR-02.12 HTTP 中介層擋的每一項，socket 側逐項核對有對應擋法 {#sr-02-12}

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

## SR-02.13 端點守門覆蓋率用程式產清單，定期對照 {#sr-02-13}

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

## SR-02.14 停權記在客戶身上，出貨版只認正式簽發站公鑰 {#sr-02-14}

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

---

*來源：[連線頁](../connections/C02-frontdoor-to-api.html)的防線（D）；去重對照在 `requirements/_drafts/D-to-SR.md`。*
