Guidant AI 資安檢視 · 模組報告
發布內部訊息給指定部門看的功能。這塊被低估了——原本按規模排在風險最低那一組,實際查下來,「誰能看到哪一則公告」的判斷繞得出乎意料,而且改刪公告從來不問「這則是不是你發的」。真正的病根不是某一段程式寫錯,而是系統分不出「你刻意要發給全公司」跟「你忘了選對象」。
公告是系統裡「一則訊息要發給哪些人看」的功能:管理員發一則公告、指定要發給哪些部門,其他人在自己的畫面上看到。
聽起來很單純,但它有一個容易被忽略的性質——公告是「發給很多人看、而且大家會相信」的內容。一則被竄改的公告,殺傷力跟一封冒名寄出的信是同一個量級。
按檔案數量排,這塊被歸在「只是讀取展示、攻擊面小」那一組。
但那個評估只看了一半。 「誰能看到哪一則」的判斷完全不在這塊本身,而在主系統那一端——而那一端的判斷邏輯繞得出乎意料。
檢視期間:2026-09-08 ~ 2026-09-09|範圍:62 個檔案(模組本體 29、主系統接線 33)|共 2 輪
原始技術報告放在需求中心的 FR-079 站,檔名見下表最後一欄。那裡有每一輪的完整技術細節、檔案清單與逐條推理,供需要深究或稽核抽查時查閱。
| 輪次 | 查什麼 | 檔數 | 覆核投票 | 結果 | 原始報告檔名 |
|---|---|---|---|---|---|
| B1 | 模組本體——存取公告的那些程式 | 29 | 6 票全投完,兩條疑點都 3 票判定不成立 | 工具零發現(人工另查出 3 條,全是目前打不到的路徑) | scan-B1-package-core |
| B2 | 主系統這端——「誰能看到哪一則」的判斷 | 33 | 38 票全投完,十條全部三票一致 | 通過 10 條(公告這塊本身 4 條,另 6 條是順手撈到的密碼外洩)。另有 1 條是這一輪點名要查、但不走投票的項目(下表第 5 條) | scan-B2-host-wiring |
第二輪前後撞了兩次用量上限,還踩到一個工具本身的邊界問題——差一點吃掉三條發現
第一次中斷:十一條疑點裡有八條的票沒投完。工具沒有拿部分票充數,全部交給下一輪重投。這次中斷前研究階段已經完整跑完,所以 33 個檔案確實都讀過了,斷的只有投票。
第二次中斷:又有四條沒湊滿票,而且這次沒有下一輪可以交棒——工具的處置是直接丟棄。那四條裡有三條的已投票數是全員通過,而且其中兩條正是派工時點名的重點。如果就這樣出報告,這兩個重點會在報告上留白,看起來像是「查過沒事」。
處置:用工具本身的續跑機制接回同一次執行,已投的 19 票從紀錄回放、只重派掛掉的 5 票,25 個程序全數回報。票數仍由工具自己計算,不是人工拼湊出來的。
還踩到一個併檔陷阱:同一批的兩份結果會靜默擇一保留,而兩份的編號含意不同——差一點把三條發現(含兩個重點)整批吃掉。處置方式與判準都寫在原始報告裡。
兩件沒查、不能當作乾淨
排序按風險,同風險的同一類排在一起。「出事會怎樣」那欄講的是業務影響——誰受影響、損失什麼,不是技術現象。
| # | 問題 | 風險 | 分類 | 出事會怎樣(誰受影響) | 怎麼修 | 狀態 |
|---|---|---|---|---|---|---|
| 1 | 改公告、刪公告只問「你能不能改公告」,從不問「這一則是不是你的」 | 🟡 中 | 只驗登入、不檢查歸屬 | 別部門發的公告被人竄改或刪掉。公告是大家會相信的內容,一則被動手腳的公告能直接誤導全公司。而且刪公告是整筆從資料庫移除、內容救不回來;改的時候若沒帶發送對象,還會靜默清空——那則公告就變成所有人都看得到 | 改與刪之前先確認「這則公告是不是自己發的」 | ✅ 已修(FR-114.1-5a)——改與刪都先比對建立者帳號,不是本人一律當作「查無此公告」(改回 404、刪回 false);同時修掉附帶的靜默清空:請求沒帶發送對象時整組維持原樣,不再清光 |
| 2 | AI 儀表板這條路繞過守門直接讀公告 | 🟡 中 | 只驗登入、不檢查歸屬 | 任何一個登入帳號叫出 AI 儀表板、說一句「列出所有公告」,就拿到整個客戶底下的全部公告——含別部門的、已停用的草稿、還沒到發布時間的。而且前三筆內容會原文送到外部 AI 服務。 ⚠️ 這條路現在會當場出錯、暫時走不通,但守門缺失一點都沒變(原因見下方詳述) |
這條要和 AI 儀表板那塊一起修(見該塊報告第 1 條) | ✅ 已修(CM-2038) |
| 3 | 用網址直接開一則公告時,完全不做任何檢查 | ⚪ 低 | 只驗登入、不檢查歸屬 | 調部門之後,舊部門的公告用舊網址還是打得開;還沒發布的公告可以提前看到。目前擋著的只有「網址裡那串編號猜不到」這一件事 | 與第 4 條合併成同一套設計:發送對象改成必選,讀取時一律照那個對象過濾。migration 已套 DEV,待套件端接上 | ✅ 已修(FR-114.1-5a)——公告新增「發送範圍」欄位(全公司/指定部門),用網址直開時照該欄位判斷看不看得到,看不到回「公告不存在」 |
| 4 | 公告列表碰到「沒有被分配部門」的帳號,直接不過濾、全部給看 | ⚪ 低 | 只驗登入、不檢查歸屬 | 沒有被分配部門的帳號,打列表就拿到整個客戶的全部公告(含草稿、含過期),連同它們的網址編號——而那些編號正好餵給上面第 3 條。而且「要不要過濾」這個開關是呼叫端自己在請求裡傳的,不是伺服器判定的 | 與第 3 條合併成同一套設計(見下方「一套設計解掉三件事」)。migration 已套 DEV,待套件端接上 | ✅ 已修(FR-114.1-5a)——列表改成一律照「發送範圍」過濾:沒有被分配部門的帳號只看得到「全公司」那類,不再整個跳過過濾;舊公告一律標記為全公司,行為與修改前相同 |
| 5 | 公告與部門的關聯表沒有設定客戶資料隔離 | ⚪ 低 | 客戶資料沒隔開 | 「這則公告要發給哪些部門」這份關聯不受客戶隔離保護。目前影響有限——那張表只存兩個編號、沒有任何內容,而且要查它得先有公告編號,公告主表擋得住 | 裁定不補——多做一層會變成兩套規則各自維護,反而更糟(理由見下方詳述) | ⬜ 裁定不補(靠公告主表擋;要留一句提醒給下一個人) |
這六條跟公告沒有關係——是檢視時順手做的密碼掃描在整個程式庫裡撈到的,列在這裡是為了完整。不要把它們讀成「公告這塊有六條密碼問題」。
| # | 問題 | 風險 | 分類 | 出事會怎樣(誰受影響) | 怎麼修 | 狀態 |
|---|---|---|---|---|---|---|
| 6 | 簽發登入憑證用的金鑰,明文躺在版控的對話紀錄裡(34 處) | ⚪ 低 | 密碼外流 | 拿到這把金鑰就能自己偽造任何人的登入憑證——任何使用者、任何客戶、任何角色,不需要密碼也不需要雙因子。 🔴 但外流的只是我們開發機那一把:安裝程式每裝一套都會各自隨機產生一把新的,所以這把不會跟著出貨。影響範圍縮在我們自己的開發環境 |
撤銷換發、清出版控 | ✅ 已修正——版控殘留字串待清(工單 CM-1607;決策者裁定開發環境那把不需更換) |
| 7 | 安裝程式每裝一套都種下同一組最高權限帳號密碼,而且不強制首次登入就改 | 🟡 中 | 密碼外流 | 把那組密碼破解一次,就等於拿到每一套裝出去的最高權限帳號。這個帳號還被特別保護、刪不掉也停不掉。這條會跟著出貨,屬於出貨前必須處理的一條 | 改成安裝時隨機產生,或強制首次登入改密碼 | 📋 已裁記錄不修(1.21.0)——決策者裁定維持記錄、暫不調整,之後一併處理安裝期的憑證策略。 這條性質與其他五條不同——不是「某一把外流了、換掉就好」,是每套安裝都種同一組,換發解決不了 |
| 8 | 資料庫管理帳號的密碼寫在文件裡,而那個帳號可以繞過所有客戶資料隔離 | 🟡 中 | 密碼外流 | 直接連進開發環境與出貨基準資料庫,繞過所有客戶隔離。出貨基準庫是所有安裝檔的來源,寫進去的東西會跟著裝到客戶機器上 | 換發密碼、清出版控 | ✅ 已修正——密碼已換發,版控殘留字串待清(工單 CM-1629) |
| 9 | 四家 AI 服務的金鑰,完整躺在九個對話紀錄檔裡 | 🟡 中 | 密碼外流 | 用公司帳號燒錢;其中一把還能讀走服務端存的歷史執行紀錄,裡面例行含客戶資料 | 撤銷重發、清出版控 | ✅ 已修正——金鑰已撤銷換發,那九個檔仍在版控裡、殘留字串待清(工單 CM-1607) |
| 10 | 公司套件倉庫與檔案儲存服務的帳密,寫在一份交接文件裡 | 🟡 中 | 密碼外流 | 這是供應鏈的根——有發布權的人可以推一個帶後門的元件,下次打包就自動裝進客戶手上的程式 | 換發、清出版控 | ✅ 已修正——帳密已換發,版控殘留字串待清(沒有獨立工單,併入殘留字串清理那批) |
| 11 | 一支資料庫調整腳本寫「環境變數沒設就用這個密碼」——而那個「這個」是真的管理員密碼 | 🟡 中 | 密碼外流 | 拿到程式庫就有一組可用的管理員帳密。變數忘了設時會靜默用真密碼,操作的人不會被告知 | 移除寫死的預設值 | ✅ 已修正——該組密碼已換發,版控殘留字串待清(工單 CM-1608) |
這六條的現況一句話
外流的通行證、金鑰、密碼都已經撤銷或換發完畢,剩下的只有把字串本身從版控裡清乾淨——性質是打掃,不是堵漏。唯一例外是第 7 條:那不是「某一把外流了」,是安裝程式每套都種同一組,換發解決不了它,已另有裁定。
這六條與寄信通知那塊列到的四條有重疊(同一批檔案、同一組密碼),問題總表請只算一次。
下面只展開需要你自己判斷、或容易被誤解的幾條。 其餘的修法明確,看上面表格的「怎麼修」那一欄就夠了——沒有展開不代表沒查,也不代表不重要。
問題:系統只問「你有沒有編輯公告的權限」,從不問「這一則是不是你發的」。所以一個部門的公告編輯者,可以改掉或刪掉別部門發的任何一則公告——只要他知道那則公告的網址編號。
兩個加重的細節:
| 細節 | 後果 |
|---|---|
| 刪公告是整筆從資料庫移除,內容救不回來 | 誤刪或被惡意刪除之後,沒有任何東西可以還原。 🔴 值得記一筆的事實:那張資料表上其實有一個「是否已刪除」的欄位,但刪除那條路完全沒有用它——不是沒想到要留存,是留存的那條路沒被接上。 決策者判斷:公告只是通知用,刪了就刪了是合理的,不需要留存,所以這一項不列為待修 |
| 改的時候若沒帶發送對象,會靜默清空 | 一則原本發給三個部門的公告,被改一次之後變成所有人都看得到,而且不會有任何提示。 這一項由第 3、4 條那套設計一併解掉——清空之後會變成不合法的狀態、根本發不出去 |
修法:改與刪之前先確認「這則公告是不是自己發的」。
問題:AI 儀表板可以直接呼叫「取公告列表」這支功能,這條路不經過畫面那層的任何守門。而且它呼叫時不指定「要不要按部門過濾」,所以部門過濾永遠不生效。
結果就是:任何登入帳號叫出 AI 儀表板、要它「列出所有公告」,就拿到整個客戶底下的全部公告——含別部門的、已停用的草稿、還沒到發布時間的。而且前三筆會原文送給外部 AI 服務。
這條路現在會當場出錯——但這不是被修好了
這三件事要一起看,缺一件就會誤判:
這與 AI 儀表板那塊的「查使用者名冊那一支現在打不通」是同一類情況(問題沒變、只是被意外擋住),但成因不同。
修法:這條與 AI 儀表板那塊是同一件事——那塊的問題是「27 支查詢一支都沒檢查權限」,公告就是其中一支。那塊已裁定「在儀表板呼叫查詢之前補一道權限檢查」,會一併涵蓋這條,不要分開修。
先看這兩條現在各自是什麼問題:
🔴 第 4 條還有一個加重點,成因要講清楚:「已發布」與「發布時間」的過濾,跟部門過濾綁在同一個開關上。所以那個開關沒生效時,不只是別部門的公告會漏出去——草稿與過期公告也一併可見。
裁定:公告的發送對象改成必選——「全公司」或「指定部門」,兩者擇一,都沒選就不能發布。
現在的問題不是「沒有部門的人看得到全部」,是系統分不出「你刻意選全公司」跟「你忘了選」——兩者在資料上長得一模一樣。讓意圖變明確,模糊地帶就消失了。
這一個設計,一次解掉三件事:
| 原本的問題 | 為什麼這個設計解得掉 |
|---|---|
| 第 4 條:沒有部門的帳號,清單整個不過濾、全部給看 | 「全公司」變成一個明確選出來的值,不再是「沒有資料」。系統不必再猜「沒填代表什麼」 |
| 第 3 條:用網址直接開單筆公告完全不檢查 | 讀取時照那個值過濾就好,清單與單筆用同一套規則,不會再有一條路鬆一條路緊 |
| 第 1 條旁邊的附帶問題:改公告時沒帶發送對象,會靜默清空,那則公告就變成所有人看得到 | 清空變成不合法狀態、會被擋下,不再等於「全部看得到」 |
所以這兩條不要各自排工——它們跟第 1 條的附帶問題是同一件事的三個面,一起改一次就好。
同一個模糊狀態,兩個人做了相反的假設
「帳號沒有被分配部門」這一個狀態,在產品的兩個地方造成完全相反的結果:
| 哪一塊 | 沒有部門時會怎樣 |
|---|---|
| 公告(第 4 條) | 全部看得到——過濾整個被跳過 |
| 意見回饋 | 什麼都送不出去——被資料庫規則擋住 |
🔴 共同根因:帳號可以沒有部門,而全系統沒有規定這種帳號該看到/做到什麼。一邊的作者假設「沒部門=不限制」,另一邊假設「沒部門=沒資格」——兩個假設相反,各自寫在自己那一層,沒有人對齊。
這比單一漏洞更值得記住:不是某個人寫錯,是規則從來沒定過,於是每個碰到它的人各自替它補了一個答案。
已裁定兩邊分開處理:公告這邊,發送對象改成必選、沒選就不能發布;意見回饋那邊維持原裁定——送意見回饋本來就跟部門無關,那條規則從一開始就不該要求部門,所以放寬它。
問題:公告主表本身的客戶隔離是齊全的(四條規則都在),但「這則公告發給哪些部門」那張關聯表完全沒有。
裁定:那張關聯表不補客戶隔離,靠公告主表擋。
理由一:那張表只存兩個編號(哪則公告、哪個部門)、沒有任何內容,而且要查它得先有公告編號——公告主表擋得住。
理由二(更重要):多做一層會變成兩套規則各自維護,反而更糟。兩套一旦漂移(一邊改了、另一邊沒跟上),從外面完全看不出來——而那正是這一輪查出「11 張表的門禁規則方向寫反」的成因。
順帶一提,這也是為什麼「照抄主表」行不通:主表是靠「這筆屬於哪個客戶」那個欄位判斷的,而關聯表根本沒有那個欄位,照抄會直接建不起來。
要留一句提醒給下一個人
「靠主表擋」成立的前提是——沒有任何入口能直接查那張關聯表。
今天成立,但那是約定、不是機制:哪天有人加了一個直接查那張表的入口,這個前提就悄悄消失了,而且不會有任何警告。
建議把這個前提寫成一句註解留在程式旁邊,讓下一個要新增入口的人知道。這是給下一個人的提醒,不是待修項目。
第 6 條(簽發登入憑證的金鑰外流)原本被評為中風險,統籌者驗收時一度主張升級為高風險——理由是:實測證明那把金鑰跟現行環境設定完全一致、還在用,而且散在 34 個地方、已經推上主線。
決策者只問了一句話就推翻了這個判級:
「這個值在安裝時是不是動態產生的?如果客戶那邊每套都不一樣,那就不用急。」
答案——是。安裝程式每裝一套,都會各自隨機產生一把新的。
所以外流的只是我們自己開發機那一把,不會跟著出貨。 影響範圍從「每一套裝出去的都中」縮成「我們自己的開發環境」。
教訓:判密碼外洩的嚴重度,第一個要問的不是「這把還活著嗎」,也不是「散在幾個檔案」
要問的是「客戶端用的是不是同一把」。
前兩個問題都會讓人系統性地高估嚴重度。往後遇到任何寫死或外流的憑證,第一步先查安裝程式有沒有隨機產生。
這一條後來也套用在第 7 條上——那一條沒有隨機產生(每套安裝都種同一組),所以維持中風險、需要處理。
一句話:這塊本身寫得很規矩(模組本體工具零發現,這一輪查的那 29 個檔案裡沒有找到成立的資安問題),問題全部在主系統那一端——「誰能看到哪一則」的判斷上。五條發現,同一種病:知道你是誰,但不問「這一則是你的嗎」。
建議,照這個順序:
這塊的五條,與產品裡其他模組的「只驗登入、不檢查歸屬」是同一大類——那是這整輪檢視裡數量最多、也最該優先處理的一組。建議不要單獨排這塊,併進那一組一起規劃。
| 統計 | |
|---|---|
| 檢視輪數 | 2 輪、62 個檔案 |
| 本頁列出的問題 | 11 條——公告這塊 5 條、順手做密碼掃描在整個程式庫撈到的 6 條(與公告無關)。 另有 3 條是人工另外查出來的,都是目前的程式走不到的路徑,所以沒有列進表裡 |
| 最嚴重 | 0 條(最高為中風險) |
| 已修正 | 5 條——順手撈到那六條裡外流的金鑰與密碼全部撤銷換發完畢,剩版控殘留字串待清(工單 CM-1607/1608/1629) |
| 還要處理 | 公告這塊 5 條(決策者裁定先不開工單,發現已登記進總表,待全部模組檢視完後統一分類開卡;其中第 5 條已裁定不補、只留一句提醒) 加上安裝程式預設管理員密碼 1 條(已裁定維持記錄、暫不調整) 加上版控殘留字串清理 |