---
title: 系統設定
eyebrow: Guidant AI 資安檢視 · 模組報告
h1: 系統設定（jedi-system-core）
lede: 這塊存的不是客戶資料，是**系統自己的鑰匙**（寄信、員工帳號目錄、檔案儲存的伺服器帳密）與**全站生效的總開關**。風險形狀跟其他塊不同——別塊問的是「誰能看誰的資料」，這塊問的是「誰能改全公司的規則」。
---

## 這塊在產品裡做什麼

產品裝好之後，總要有人告訴它：信要從哪台伺服器寄出去、員工帳號要去哪裡驗證、上傳的檔案要放進哪個儲存空間、密碼要多長才算合格、登入幾次失敗要鎖帳號。**這些設定就放在這一塊。**

畫面上它長得很平凡——幾頁設定表單，填一填按儲存。但填進去的內容有兩種，都很敏感。

::: grid2
::: {.card .plain}
#### 它平常在做什麼
1. 存放**系統對外連線用的帳號密碼**：寄信伺服器、員工帳號目錄、檔案儲存空間
2. 存放**全站生效的規則**：密碼強度、登入失敗鎖定次數、登入憑證有效期、雙因子驗證開關
3. 另外管一份系統選單的字典（哪些功能出現在哪個選單下）
:::
::: {.card .crit}
#### 為什麼這塊特別要緊
**這裡放的是鑰匙，不是資料。**

一把寄信密碼外流，別人能用公司名義寄釣魚信；一把檔案儲存密碼外流，**所有客戶上傳的檔案都在他手上**。

而改設定是**全站生效的總開關**——改掉員工帳號目錄的位址，等於把全公司的登入驗證導去別人的機器。
:::
:::

---

## 檢視軌跡

**檢視期間**：2026-09-15｜**範圍**：82 個檔案（模組本體 57、接線 25）｜**共 3 輪**

> **原始技術報告**放在需求中心的 [FR-096 站](https://guidantai-feature-doc.jedicotech.com/FR-096-2609-system-core-security-scan/)，檔名見下表最後一欄。那裡有每一輪的完整技術細節、檔案清單與逐條推理，供需要深究或稽核抽查時查閱。

| 輪次 | 查什麼 | 檔數 | 覆核投票 | 結果 | 原始報告檔名 |
|---|---|---:|---|---|---|
| **P1** | 設定怎麼存、怎麼讀、誰能改——模組本體這一側 | 24 | 12 票全投完，**否決一半** | 找到 2 條（1 高 1 低） | `scan-P1-config-chain` |
| **H1** | 我們主系統這端怎麼把它接上來 | 25 | 6 票全投完，**兩條都三票一致** | 工具報 2 條、其中 1 條是前一輪同一件事，**真正新增 1 條高風險** | `scan-H1-host-wiring` |
| **P2** | 系統選單的字典 | 33 | 3 票全投完，**唯一一個疑點被三票一致否決** | **這一輪沒有找到問題** | `scan-P2-menu-dictionary` |

::: {.callout .ok}
**第一輪的否決率是一半，這件事值得講**

工具提了四條，覆核把其中兩條打掉。**否決理由具體到打開檔案指出「這個資料結構只有六個固定欄位，多送會直接報錯」。**

願意否決，通過的那兩條才有份量。
:::

::: {.callout .warn}
**三輪各有一處要誠實交代**

- **第一輪**：工作單上列了八個要追查的疑點，工具只碰到其中五項。另外三項屬於「**沒被提出**」，不是「查過沒問題」。
- **第二輪**：工作單列的八個追查點，工具四項完全沒碰、三項只碰一半——**其中沒碰到的第一點，還是工作單自己標成「本輪最重要」的那一條**。這一輪的報告也不是執行者交的，是統籌者事後補寫的。
- **第三輪**：工具沒有回報逐檔的閱讀紀錄，**無法證明 33 個檔案每一支都被讀過**。

三輪的實質結論都是靠統籌者逐條開檔核對＋連開發環境查資料庫補起來的。
:::

::: {.callout .ok}
**第三輪「沒找到問題」，但有一件事記下來了**

被否決的那條是「前台的下拉選單只過濾了『啟用』、沒過濾『公開』」。否決理由是：**標成「私有」的資料一列都不存在，也沒有任何一條路會生出來。**

統籌者從四個來源逐項復核（出貨預設資料 74 筆全部公開、欄位預設值三處都是公開、寫入端兩支都要平台管理員、開發環境實查 74 筆中私有 0 筆），同意否決。

**但仍然記一筆：擋住它的是「現在沒有那種資料」，不是程式。** 哪天平台管理員標了一列私有，那一列就會立刻出現在所有登入者的下拉選單裡。修法只要一行。
:::

---

## 問題一覽

**排序按風險，同風險的同一類排在一起。**「出事會怎樣」那欄講的是**業務影響**——誰受影響、損失什麼，不是技術現象。

| # | 問題 | 風險 | 分類 | 出事會怎樣（誰受影響） | 怎麼修 | 狀態 |
|---|---|---|---|---|---|---|
| **1** | **一個客戶的管理員，可以改掉全公司所有人的登入規則，還能把登入驗證來源指到自己的機器上** | 🟠 高 | 只驗登入、不檢查歸屬 | **開一個新客戶就自動多一個能改全公司規則的人**。他可以關掉全公司的雙因子驗證、讓帳號永遠不會鎖定（等於可以無限次猜密碼）、把登入憑證有效期從五分鐘延到一個月。**更嚴重的另一半是把員工帳號目錄指到自己的機器——那是直接接管所有人的登入**，而且改壞了看不出來 | **已拍板**：只開放給客戶自己組織樹第一層（總部）的管理員改，子公司的管理員改不到；三支寫入路徑都要補上這個判斷。實際做法：新增租戶層級判斷，全公司共用的三組設定只開放總部管理員寫入，讀取不變 | ✅ **已修（CM-2054）** |
| **2** | **任何登入帳號打一支查設定的功能，就拿到檔案儲存的帳號密碼明文** | 🟠 高 | 只驗登入、不檢查歸屬 | **不必是管理員、不必有任何權限、不必知道任何編號，一個請求就拿到可讀寫所有客戶上傳檔案的鑰匙。** 拿到之後可以完全繞過我們的系統直連儲存空間——那時候程式裡補再多檢查都沒用。同一條路還會吐出寄信與員工帳號目錄的位址、埠號、登入帳號 | 讀取端比照同一支檔案裡的寫入端補上權限檢查，**三個入口都要補、補一個沒有用** | ✅ **已修（CM-2063）** |
| **4** | **第 1 條的修法裡「誰算總部」的判斷，擋得住自己公司的子公司，擋不住另一家不相干客戶的總部** | 🟠 高 | 客戶資料沒隔開 | 寄信設定、員工帳號目錄設定全站只有一份；**客戶 B 的總部管理員照樣能改掉客戶 A、甚至原廠平台本身在用的寄信伺服器與帳號目錄**——後果與第 1 條相同，只是換一種身分打進來。統籌者在開發環境用實際的公司路徑測過 | 決策者 09-26 裁定：落地版只有「安裝時建的那家客戶」算總部，SaaS 版只有原廠能改 | ✅ **已修**（CM-2202，commit `6cfcb116e`，1.21.0 出貨） |
| **3** | 密碼遮蓋名單漏掉檔案儲存那一組 | ⚪ 低 | 密碼外流 | **就算第 2 條補好了，有正當權限的客戶管理員打開「儲存設定」頁時，瀏覽器仍會收到那把共用密鑰的明文**——任何看得到他瀏覽器流量、存檔或前端錯誤紀錄的人都拿得到 | 把儲存設定加進遮蓋名單、把帳密欄位名加進要遮蓋的清單，並改用寄信與員工帳號目錄那套「不回密碼、沒帶就沿用」的做法。實際做法：儲存設定的密鑰納入遮罩名單，並補上「沒帶就沿用原值」避免存檔抹掉密鑰 | ✅ **已修（CM-2054）** |

---

## 第 1 條：改到的是全系統共用的那一份 {#shared-config}

::: {.callout .crit}
**這條的形狀，跟前面幾塊都不一樣。**

前面幾塊問的是「A 客戶能不能看到 B 客戶的資料」。**這一條是：一個客戶的管理員，改到的是全公司共用的那一份規則。**
:::

### 問題是什麼

產品把「修改登入安全政策」這件事當成**客戶自己的事**，所以權限被標成客戶層級——**每開一個新客戶，那個客戶的管理員就自動拿到它**，不需要任何人手動指派。

問題是：這支功能寫入的**不是那個客戶自己的設定，而是全系統共用的那一列**。

**客戶資料隔離為什麼擋不住**：因為寫入那段程式為了寫得進共用那一列，**程式裡明文把隔離關掉了**。

### 更嚴重的另一半

完全相同的形狀，也套在**員工帳號目錄**與**寄信伺服器**設定上。

| | 改壞之後 | 嚴重程度 |
|---|---|---|
| **登入安全政策** | 全公司的雙因子驗證被關掉、帳號永遠不鎖、憑證有效期拉到一個月 | 改壞了**看得出來** |
| **員工帳號目錄** | **全公司的登入驗證來源被指到攻擊者自己的機器** | 直接**接管所有人的登入**，而且**看不出來** |
| **寄信伺服器** | 系統寄出的信經過攻擊者的機器 | 密碼重設信等敏感內容全部經他手 |

**第二列比第一列更該先修。**

### 開發環境實查佐證

這條原本只是讀程式碼推出來的，統籌者連開發環境查了資料庫（唯讀），四項全部對上：

| 查什麼 | 結果 |
|---|---|
| 這三個權限是不是客戶層級 | **是**，三個的平台層旗標都是關的 |
| 有哪些客戶的管理員實際拿著 | **開發環境裡的測試客戶，每一個都有**（這些是我們自己建來測試用的，不是真實客戶；重點在於「有建就有拿到」這件事成立） |
| 共用那一列在開發環境有幾筆 | 7 筆，全部屬於總部 |
| 新客戶開通會不會自動發 | **會**——開通程式的邏輯是「把權限全集扣掉平台層的，其餘全部發給該客戶的預設管理員」 |

**本報告撰寫時逐處開檔核對，三支寫入路徑與繞過隔離的那段程式都還在原地，一行未改。**

### 怎麼修（已拍板）

::: {.callout .decided}
**已拍板：只開放給客戶自己組織樹第一層（總部）的管理員改**

**問題的本質不是「客戶不該能改」，是「改的人層級不對」。** 這幾項設定（登入規則、員工帳號目錄、寄信伺服器）本質上是「一整家客戶共用的基礎設施」，不是每個子部門各自的東西——正常情況下子公司也不會自己接一套獨立的員工帳號目錄。所以修法是**把權限收到總部那一層**：客戶調整自己登入強度的自主權**完全保留**，只是改的人必須是總部的管理員，子公司的改動不會再波及全公司。

**怎麼做**：三支寫入路徑都加上「這個人所屬的單位是不是客戶組織樹的第一層」這個判斷。平台管理員身分更高、自然也改得到，不需要另外加條件。

⚠️ 這裡說的「第一層」指**客戶自己組織樹的第一層（總部）**，跟系統內建、只給平台方後台用的那個最上層是兩回事，那條界線維持不動。

**考慮過但未採用的三個方向**

| 方案 | 為什麼不採用 |
|---|---|
| **① 寫入時要求平台管理員** | 會讓客戶完全不能自主。產品裝在客戶自己的機房、主機權限都在他們手上，卻要為了改一下密碼長度打電話給原廠——這不合理，也不是客戶會接受的產品形態 |
| **② 每個客戶一份自己的設定** | 方向本身乾淨，但要動資料表結構，成本比其他方向高一個量級 |
| **③ 把這三個權限改成平台層** | 效果等同 ①，客戶一樣失去自主權 |
:::

---

## 第 2 條：一支查詢就拿到儲存空間的鑰匙 {#storage-cred}

### 問題是什麼

讀取系統設定有三個入口，**三個都只檢查「你有沒有登入」，完全不檢查權限**。

**這是漏掉、不是刻意**：同一支檔案裡的新增、修改、刪除**每一個都有權限檢查**。檔案自己的說明文字也承認「讀取只要登入即可」。

### 客戶資料隔離為什麼擋不住

隔離擋的是「看到別家客戶那一列」。可是——

**每個客戶自己那一列裡，裝的是同一把鑰匙。** 安裝時產生一組，之後每建一個新客戶就原封不動複製一份過去。

統籌者連開發環境實查（只比對雜湊值、沒有印出明文），**確認不同客戶那幾列的密鑰完全相同**——「複製給新客戶」從推測變成實證。（查的是開發環境裡我們自己建的測試客戶。）

::: {.callout .warn}
**開發環境有幾筆那一欄是空的，原因要講清楚**

開發環境那七筆（全是我們自己建的測試客戶）裡，有幾筆密鑰欄位是空的。**那不是因為沒複製，是因為負責複製的程式「失敗不擋建立」**——開發環境沒有真的接上檔案儲存，它就靜靜跳過了。

**只要儲存空間有正常設定好，那一欄就會有值。**
:::

### 影響

| | |
|---|---|
| **要先有什麼** | 一個能登入的帳號。**不必是管理員、不必有任何權限、不必知道任何編號** |
| **拿得到什麼** | 檔案儲存的位址、帳號、密碼明文；順帶還有寄信與員工帳號目錄的位址、埠號、帳號 |
| **最麻煩的地方** | 拿到之後可以**完全繞過我們的系統**，用標準工具直連儲存空間，把所有客戶的檔案列出、下載、覆蓋、刪除 |

**本報告撰寫時開檔核對，模組本體那兩個入口與主系統那個入口都還是只驗登入。**

### 怎麼修

| 步驟 | 做什麼 | 為什麼不能省 |
|---|---|---|
| 一 | 三個入口各補上按設定類別分流的權限檢查 | **只補一個沒有用**，另外兩個照樣拿得到 |
| 二 | 補的時候沿用同一支檔案既有的「先擋權限、再回報找不到」順序 | 順序對調的話，沒權限的人可以用不存在的編號試探出「這筆存不存在」 |
| 三 | 第 3 條的遮蓋名單一起補 | 權限補好了明文照吐，只修一邊等於沒修乾淨 |

**所需的權限項目已經存在，不必新建**——這是接線，不是造新東西。

::: {.callout .warn}
**「密碼一律不回傳前端」那條原則，解決不了這一條**

檔案上傳那塊已經拍板一條全站原則：**設定類的密碼欄位預設一律不回傳給前端**。很容易以為套了這條，這裡就沒事了——**不是的**。

- 那條原則管的是「**回什麼**」，它蓋的是本頁**第 3 條**（明文密碼被送到瀏覽器）。
- 這一條的問題是「**誰能問**」。就算密碼真的不回傳了，這支查詢**還是任何一個登入帳號都能打**——寄信伺服器與員工帳號目錄的位址、埠號、登入帳號照樣整批攤開。對想攻擊的人來說，這些資訊本身就已經很有用。

**所以這一條的修法不變：三個入口都要補上權限檢查。** 兩條要各修各的，不能互相取代。
:::

---

## 第 3 條：遮蓋名單漏掉一組（低）

系統本來有一道「回傳設定前先把密碼欄位刪掉」的遮蓋機制，靠兩份名單決定要遮什麼。**兩份都漏掉檔案儲存**：類別名單只有寄信、第三方登入、通知、問題單整合四組；要遮的欄位名只寫了兩個，**不包含檔案儲存實際使用的那兩個欄位名**（本報告撰寫時連開發環境確認，實際欄位名確實不在名單裡）。

**寄信與員工帳號目錄是怎麼做的**：密碼從來不回傳，前端要表示「密碼沒改」就送一個旗標、後端沿用舊的。**儲存設定沒有跟上這套做法。**

::: {.callout .warn}
**這條正好是「標記制優於名單制」的實證**

檔案上傳那塊拍板的原則是：**預設全部遮住，明確標記「這個欄位可以公開」的才回傳**——而不是維護一份「哪些要遮」的名單。這一條就是名單制出事的實例：兩份名單（哪幾類設定要遮、哪些欄位名要遮）**都漏掉了檔案儲存這一組**，而寄信與員工帳號目錄兩組剛好都有登記、所以沒事。

兩種做法漏掉時的後果差很多：**名單制漏登記 ＝ 密碼直接外洩；標記制漏標記 ＝ 頂多前端少看到一個欄位。** 這就是那條原則該套用到全站的理由。
:::

⚠️ **這是有意識的契約變更、不是單純補漏**——名單上方的說明文字明寫「凍結」，動它之前要確認沒有別的地方依賴現在這份名單。**記得同步改測試**，模組測試裡有一條把現在這份名單釘死了。

---

## 這種形狀不只出現一次 {#same-shape}

::: {.callout .crit}
**「客戶層級的權限，改到的卻是全系統共用的那一份」——這個形狀在日誌那塊也出現了一次。**
:::

日誌那塊的情況是：「修改日誌要送去哪台伺服器」這個權限同樣被標成客戶層級、同樣每開新客戶就自動發下去，而寫入時把客戶欄位寫死成空值——**等於一個客戶的管理員可以把全公司的日誌改送到他自己的機器。**

兩條是同一種病的兩個實例。**這代表它不是單一事件，是一種會重複發生的設計模式**——把某個設定當成「客戶自己的事」而發出權限，但那份設定實際上全系統只有一份。

**已拍板**：日誌那塊的這一條，與本頁第 1 條**當成同一件事處理、套用同一條規則**——一樣只開放給客戶組織樹第一層（總部）的管理員改。

**建議**：修的時候兩條一起修，並且**回頭檢查還有沒有第三個、第四個**——判準是「這個權限是客戶層級的嗎？它寫入的那份設定，全系統有幾份？」

---

## 這塊的結論

::: {.callout .warn}
**一句話**：這塊找到四條，數量不多，**但它們碰到的是系統自己的鑰匙與全站總開關**——一把儲存密碼外流，所有客戶上傳的檔案都不保；一次登入驗證來源被掉包，所有人的登入都被接管。

**建議**：

1. **第 1 條的產品決策已經拍板**：只開放給客戶組織樹第一層（總部）的管理員改，客戶的自主權保留，三支寫入路徑都補上這個判斷。**其中「員工帳號目錄被指走」那一半建議先修**，因為它改壞了看不出來。
2. **第 1 條與日誌那塊的同型問題合併成一件事處理**（已拍板），同一套修法、同一條規則。
3. **第 2 條與第 3 條要一起修**——權限補好了但明文照吐，只修一邊等於沒修乾淨。要注意「密碼不回傳」那條原則只蓋得到第 3 條，第 2 條的權限檢查仍然要補。
4. **第 2 條建議與檔案上傳那塊的同一條合併**——那是同一件事的兩側，修法一樣，三個入口一起補。
5. **第 4 條提醒一件事**：「只給總部改」這個規則，在全站只有一份設定的前提下擋不住另一家客戶的總部。決策者已改裁：落地版只有安裝時建的那家客戶算總部、SaaS 版只有原廠能改。
:::

| 統計 | |
|---|---|
| 檢視輪數 | 3 輪、82 個檔案 |
| 找到的問題 | 4 條（第 4 條是主系統掃描查出的，出在第 1 條的修法本身） |
| 高風險 | 3 條 |
| 已修 | 4 條（1.21.0 出貨） |
| 未修 | 0 條 |
| 已開工單 | CM-2054、CM-2063、CM-2202 |

---

| 你想知道 | 看哪裡 |
|---|---|
| 我們用什麼方法查、結果有多可信 | [檢視方法與工具](GUIDE-01-method-and-tools.html) |
| 其他模組的檢視結果 | [回總報告首頁](index.html) |
