Guidant AI 資安檢視 · 模組報告
產品裡所有附件與稽核證據檔的存取,都走這一塊。這是高風險問題最多的一塊——四道防線曾經一道都沒有,目前補了最底下那一道。
客戶在產品裡上傳的每一份東西,都經過這一塊:稽核證據、佐證文件、報告附件、系統匯出的檔案。
存在這裡的是稽核證據。
稽核證據如果能被別人取走、竄改或刪除,整份稽核報告的可信度就沒有了——那正是這個產品存在的理由。
檢視期間:2026-09-11 ~ 2026-09-12|範圍:71 個檔案|共 3 輪
原始技術報告放在需求中心的 FR-086 站,檔名見下表最後一欄。那裡有每一輪的完整技術細節與逐條推理,供需要深究或稽核抽查時查閱。
| 輪次 | 查什麼 | 檔數 | 覆核投票 | 結果 | 原始報告檔名 |
|---|---|---|---|---|---|
| B1 | 對外的功能入口——上傳、下載、預覽、刪除怎麼進來的 | 35 | 18 票全投完 | 找到 4 條(3 高 1 中)+人工另查出 1 條 | scan-B1-http-boundary |
| B2 | 我們主系統這端怎麼把它接上來 | 10 | 36 票全投完 | 範圍內 1 條高風險+人工另查出 4 條 | scan-B2-host-wiring |
| B1b | 檔案實際存到哪裡、資料怎麼取 | 26 | 自動檢視零發現 | 人工找到 5 條,並直接查資料庫坐實了最後一道防線也沒有 | scan-B1b-storage-backends |
第三輪自動檢視「零發現」,但那一輪其實找到 5 條
這一輪是很好的例子,說明為什麼不能只看工具的結果——5 條全部是人工查出來的,包括這塊證據最扎實的那一條(統籌者與執行者各自連進開發環境,用唯讀方式查資料庫證實)。
另外,開工時列的懷疑有一條被推翻了
第一輪的工作單上自己標「最重要」的那個懷疑,查證後不成立。報告裡把「是什麼擋住了它」寫清楚了——因為擋住它的如果是巧合,哪天有人整理程式碼把巧合改掉,問題就自己回來了。
這一頁還做過第二次查證:講者逐條回程式碼核對,推翻了三條
上面三輪是檢視當下的結果。這一頁定稿前,講者把每一條拿回程式碼與資料庫再對一次,結果改掉了三條:
| 原本寫 | 查證後 |
|---|---|
| 上傳沒有單檔大小上限 | 不成立——後端有 50MB 上限,而且是請求一進來就擋 |
| 連儲存空間「設定沒填就走明文」 | 不是沒填——七筆設定全都填了,而且全都填成不加密 |
| 借用別家帳密那條「要先做產品決策」 | 不是決策題——那是修另一個錯誤時留下的過渡措施,忘了拿掉 |
這三條的完整說明在下面各自的段落裡。把推翻的過程留在報告裡是刻意的——一份只有「找到什麼」沒有「推翻什麼」的報告,反而不可信。
排序按風險,同風險的同一類排在一起。「出事會怎樣」那欄講的是業務影響——誰受影響、損失什麼,不是技術現象。
編號跳過 10 是刻意的:原本列了 11 條,第 10 條經查證不成立,說明在表格下方。其餘編號維持不動,方便跟先前的紀錄對照。
讀這張表之前要先知道一件事:產品還沒有出貨給任何真實客戶。
下面每一條講的「客戶會怎樣」,都是產品上線之後會發生的事,不是已經發生過的事。目前系統裡的資料全部是我們自己的測試資料。
這不會讓任何一條變得比較不嚴重——問題都是真的,只是還沒有人受害。這一輪檢視正是為了在出貨前把它們處理掉。
(一個例外:表裡凡是講「我們自己的文件網站/程式碼倉庫/測試機」的條目,與產品出不出貨無關——那些曝光是真的發生過的,已在各條裡標明。)
| # | 問題 | 風險 | 分類 | 出事會怎樣(誰受影響) | 怎麼修 | 狀態 |
|---|---|---|---|---|---|---|
| 1 | 拿到檔案編號就能下載別人的檔案 | 🟠 高 | 只驗登入、不檢查歸屬 | 同一家公司內任何有帳號的人,都能取走別部門的稽核證據。資料庫那層補上之後,跨客戶已經擋住了,但同一客戶內跨專案、跨部門仍然拿得到 | 取檔之前先查出這個檔掛在哪個業務物件上,再去問那個物件的權限(第 1、2、3、7 條同一套修法) | ✅ 已修(CM-2033) |
| 2 | 能替任何檔案換發一張免登入的下載通行證 | 🟠 高 | 只驗登入、不檢查歸屬 | 通行證的機制本身是紮實的(我們自己簽、只有 120 秒、綁死一個檔案與一種用途、改內容就失效),漏掉的只有一問:換發的時候不問這個檔是不是你的。所以同公司內任何有帳號的人,拿別部門的檔案編號也換得到 | 換發前補上同一道歸屬檢查(與第 1、3、7 條同一套) | ✅ 已修(CM-2033) |
| 3 | 取檔案的那層只憑編號就交出檔案 | 🟠 高 | 只驗登入、不檢查歸屬 | 與第 1 條是同一件事的另一半。這一層是已經把檔案找出來、最有條件去問「這是誰的」的地方,該補檢查的就是這裡 | 同第 1 條,在這一層補上歸屬檢查 | ✅ 已修(CM-2033) |
| 4 | 上傳的網頁檔,別人點預覽時會在我們的網域裡被當成程式執行 | 🟠 高 | 外部送什麼就收什麼 | 受害者只要點一下預覽,攻擊者的程式就在受害者的瀏覽器裡、用受害者的身分跑起來——可以偷走他的登入狀態、冒用他的身分操作。上傳者只需要一個有效帳號 | 預覽端建可預覽類型清單、清單外一律當附件下載;清單內的加上瀏覽器層的隔離指令;並由伺服器自己確認檔案內容真的是那個格式。實際做法:預覽改成白名單+驗檔頭,HTML 加 CSP sandbox,不符合就強制下載 | ✅ 已修(CM-2062) |
| 5 | 任何登入者打一支查設定的功能,就拿到雲端儲存的帳號密碼明文 | 🟠 高 | 密碼外流 | 拿到那組帳密後可以完全繞過整個系統、直接連進儲存空間——之後在程式裡補再多檢查都沒用。拿到的是自己那家公司的那一組,但落地版是一客戶一套系統,那一組就是那家的全部 | 設定類的密碼欄位一律預設不回傳;補權限檢查;並清掉寫死在程式與腳本裡的那組帳密。五個步驟有先後順序,見下方 | ✅ 已修(CM-2063) |
| 6 | 存放檔案紀錄的資料表沒有做客戶隔離 | 🟠 高 | 客戶資料沒隔開 | 這是「四道防線都沒有」的最後一道。資料庫本身也擋不住跨客戶存取 | 加客戶歸屬欄位並開啟隔離 | ✅ 已修(2026-09-16,2,718 筆全數補上客戶歸屬) |
| 7 | 任何登入者可以永久刪除任何檔案 | 🟠 高 | 只驗登入、不檢查歸屬 | 別家客戶的稽核證據會被永久刪除,而且沒有救回的機制。對稽核產品來說,證據被取走還做得下去,證據被刪就做不下去了,而且不可逆 | 同第 1 條的歸屬檢查,但要問的是「能不能刪」而不是「能不能看」;並改成可復原的刪除 | ✅ 已修(CM-2033) |
| 8 | 讀不到自己的儲存設定時,系統會去借用別家客戶的帳密 | 🟡 中 | 客戶資料沒隔開 | 系統會繞過隔離把所有客戶的設定撈出來、取第一筆用,只留一筆警告就繼續跑。實際上借到的是最早建立的那家客戶的儲存空間——檔案會存進別人家,或去別人家找檔案 | 先把「撈全部取第一筆」改成「只取系統層那一列」(一行改動,風險當場消失);等錯位檔搬完再整段拿掉 | ⬜ 未修 |
| 9 | 本機儲存刪檔時,漏清了轉檔產生的備份 | 🟡 中 | 資料清理不完整 | 使用者以為刪掉了,但轉出來的 PDF 還留在硬碟上——「刪了沒刪乾淨」對稽核產品是實質缺陷 | 補上那一步;並把兩種儲存方式的共通邏輯抽到同一處。實際做法:衍生檔清理抽成共用函式,本機儲存模式刪檔時一併清掉快取 PDF | ✅ 已修(CM-2062) |
| 12 | 原廠共用的檔案(例如範本),一般公司的登入帳號按刪除,硬碟上的檔案本體會先被刪掉,資料庫紀錄卻刪不掉 | 🟡 中 | 只驗登入、不檢查歸屬 | 刪除時是先刪檔案本體、再刪資料庫紀錄;資料庫那一步會因為不是原廠而被擋下,但檔案本體已經沒了,畫面還回「成功」。結果是原廠給所有客戶用的範本檔全部打不開,所有客戶一起受害 | 兩步:① 一般公司刪原廠共用檔直接拒絕;② 刪除順序改成先刪紀錄、成功才刪檔案本體 | ⚠️ 部分修——第 ① 步已擋住(刪除入口補了歸屬檢查、原廠共用檔一律不准刪,1.21.0 出貨);第 ② 步刪除順序還沒改,也還沒排卡。這條是主系統掃描查出的 |
| 11 | 連雲端儲存時不使用加密連線 | ⚪ 低 | 密碼外流 | 檔案內容與帳密在公司內部網路上以明文傳輸。目前七筆設定全部都明確填成不加密,不是漏填 | 出貨裝機時預設開啟加密;現有環境另外評估切換時機 | 🚫 裁定不修(CM-2063,決策者 2026-09-23) |
原本寫「上傳沒有單檔大小上限、也沒有數量上限」。前半句是錯的。
| 實際狀況 | 後端有 50MB 上限,而且是請求一進來就擋,不是讓人傳完才發現 |
| 設定在哪 | MAX_REQUEST_BODY_MB(設在 config/config.py,由 core/app_factory.py 套用,請求一進來就提前擋下) |
| 前端另有一道 | 各個上傳元件自己也有限制,10MB~50MB 不等 |
| 最外層那道確實沒擋 | 前端派送程式的設定裡沒設上限,但設定檔註解明寫是刻意交給後端把關,不是疏漏 |
屬實的只有「數量沒有上限」這半句——但整包請求受 50MB 頂著,就算一次塞很多檔,總和一樣被擋在門外。
所以這一條從「缺陷」改判為「不成立」;「數量要不要另外訂上限」降為改善建議,不列為資安問題(訂多少是業務問題,見最後的待決策清單)。
一個檔案要被不該看的人拿走,本來應該有四道關卡擋著。這塊曾經四道都沒有。
用「有人拿著一個檔案編號來要檔案」來看這四道:
| 第幾道 | 這道關卡應該問什麼 | 原本的狀況 | 現在 |
|---|---|---|---|
| 一、入口 | 你有沒有登入?這個檔案是你的嗎? | 只問了前半句 | ⬜ 未改 |
| 二、業務邏輯 | 這個編號對應的檔案,該不該給你? | 完全沒問 | ⬜ 未改 |
| 三、取資料 | 查詢時有沒有限定只能查自己的? | 沒有任何範圍限制 | ⬜ 未改 |
| 四、資料庫 | 就算前面都漏了,資料庫本身擋不擋? | 表裡連「這是哪家客戶的」欄位都沒有 | ✅ 已補 |
2026-09-16 那次改動,把客戶歸屬欄位加進資料表並開啟隔離,2,718 筆資料全部補上歸屬。
但那次改動不是為了修這個問題。 它是另一項功能開發順手做的——那項功能要拿這張表當暫存區,才需要加上這些欄位。
這帶出一個值得記住的現象
我們在複查時發現:這段期間補起來的都是資料庫那一層,應用程式那一層幾乎沒有動過。
「只驗登入、不檢查歸屬」那一整組問題(橫跨七個模組、十幾條)一條都沒有被碰過——而那正是我們自己標記為「最該優先處理」的那一組。
所以現在的狀態是:資料庫那道牆補上了,應用程式那道門還是開著的。 跨客戶被擋住了,但同一家客戶內跨專案、跨部門,仍然拿得到。
下面只展開需要你自己判斷、或容易被誤解的幾條。 其餘的修法明確,看上面表格的「怎麼修」那一欄就夠了——沒有展開不代表沒查,也不代表不重要。
這四條不是四個問題,是同一個問題的四個出口:下載、換發通行證、取檔、刪除。
所以它們是一套修法,不是四套。 分開修會變成同一段判斷抄四遍,而且一定有一個出口會漏掉。
檔案模組不知道自己保管的檔案屬於什麼,也從來沒有去問過知道的人。
它做的事只有一行——拿編號去撈,撈到就送出。
這點很重要,因為它決定了修法方向:這不是檢查寫錯、也不是規則不夠細,是「去問一下」這個步驟從來沒有被設計進去。
先排除一個直覺但是錯的修法:用「誰上傳的」來判斷
初步的想法是「取檔案前先比對這筆資料的主人」。這個方向是錯的,因為「主人」如果指的是上傳者,會同時擋錯人、也放錯人:
| 會擋錯的 | 會放錯的 |
|---|---|
| 同專案的稽核員看不到證據——他不是上傳的那個人 | 上傳者被調離專案之後,還是看得到 |
| 上傳者離職,檔案變成沒人能開的孤兒 |
正確的問法不是「誰上傳的」,是「這個檔掛在哪件事情上,那件事你有份嗎」。上傳者是誰仍然要記,但那是留紀錄用的,不能當權限判準。
| 出口 | 要問什麼 |
|---|---|
| 下載、預覽(第 1 條) | 這個檔掛著的那件事,你看得到嗎 |
| 換發通行證(第 2 條) | 同上——通行證只是換一種拿法,門檻不該比較低 |
| 取檔那一層(第 3 條) | 同上,而且這一層是最該補的地方(見下) |
| 刪除(第 7 條) | 這個檔掛著的那件事,你改得動嗎 |
前三個問「看得到嗎」,刪除要問「改得動嗎」。 同一個專案裡,能看證據的人通常比能刪證據的人多很多——刪除如果照抄下載的判斷,等於把刪除權放給所有看得到的人。
這裡要更正一個先前講錯的說法
先前說「歸屬欄位已經在表裡了,只是沒拿來用」——這句不實。檔案那張表上沒有「掛在哪個業務物件」的欄位:
| 欄位 | 實際是什麼 |
|---|---|
那個看起來像關聯的欄位(ref_id) |
是「轉檔產生的 PDF 指回原始檔」的自我參照,全庫 2,821 筆裡只有 2 筆有值 |
上傳者欄位(owner_user_id) |
96% 是空的,而且目前沒有任何規則在用它 |
真正的歸屬關係在下游——是那些表反過來指著檔案:
| 哪張表指著檔案 | 代表什麼檔 |
|---|---|
job_evidences.file_id |
稽核證據 |
ssp_reference_documents.file_id |
安全計畫的佐證文件 |
issue_upload_file_mapping.file_uid |
意見單的附件 |
這個更正對成本判斷很重要:歸屬資料一直都在,不需要新建欄位、不需要回填舊資料——只是從來沒有人去問過它們。
一個很自然的顧慮是:「難道要為每一種檔案各開一支下載功能、各做一套權限檢查?」
不用。 形狀是這樣:
| 誰 | 做什麼 |
|---|---|
| 檔案模組 | 留一個空位,說「我需要一段能回答『這個人能不能拿這個檔』的程式」 |
| 各業務模組 | 把自己的那段回答程式登記進來 |
| 要取檔的時候 | 檔案模組先查出這個檔屬於哪一類,再去呼叫對應的那一段 |
檔案模組不懂任何業務——它只做三件事:問、等答案、照做。
各業務模組的回答程式只回答兩件事:
第二題各業務模組本來就會算——只是從來沒有人來問過它。
那份「登記」是程式裡的一張對應表,不是資料庫的表
大約十幾列。做成資料庫的表會出問題,因為規則是活的邏輯(要查成員、看角色、算狀態),做成資料表等於用資料庫寫程式。
擴充成本:新增一種檔案用途時只加一列登記,檔案模組零改動;資料庫零改動、舊資料零回填。
有三條規則要定死,否則這套修法會有破口:
| 規則 | 為什麼 |
|---|---|
| ① 查不到登記的類型 → 拒絕(不是放行) | 漏登記時往「看不到」倒,不是往「全都看得到」倒 |
| ② 沒有任何模組認領的檔 → 拒絕 | 孤兒檔不該因為沒人管就人人可取 |
| ③ 拒絕時回「找不到這個檔案」而不是「你沒有權限」 | 後者等於告訴對方「這個編號是有效的」,可以拿來一個一個試 |
這個形狀與產品既有的把關方式是一致的:「只看你是誰」的檢查掛在入口,「要先找出是什麼東西才知道該問誰」的檢查放在業務邏輯裡。檔案權限屬於後者,是目前唯一沒接上的一塊。
產品有個功能:可以替一個檔案換發一張「通行證連結」,讓瀏覽器直接開啟檔案不用再登入一次。這個設計本身是合理的,而且做得相當紮實:
| 這張票 | 實際狀況 |
|---|---|
| 誰簽的 | 我們自己簽的,不是雲端儲存商的預簽網址 |
| 有效多久 | 120 秒 |
| 能開什麼 | 綁死一個檔案、一種用途,換不了別的 |
| 能不能偽造 | 有簽章,內容改一個字就失效 |
漏掉的只有一問:換發的時候不檢查這個檔是不是你的。 只要有帳號,拿任意檔案編號都換得到——所以它跟第 1、3、7 條是同一個病,補的是同一道檢查。
這裡刪掉了一個先前的論述:「稽核證據可以被合法帶出公司」
先前把第 2 條評得比第 1 條更嚴重,依據是「換出來的連結可以傳給公司外部的人」。
這個依據不成立。 一個人只要下載得到檔案,本來就能轉寄、存檔、拍照——任何系統都擋不住。票能不能給外人開,對「檔案會不會外流」沒有增加任何風險。 留著這句話反而給人挑毛病的把柄。
「那第 2 條要不要降級」——已裁定:不再單獨排級。
決策者 2026-09-20 裁定:第 2 條與第 1、3、7 條是同一個洞的第四個出口(都是「換發/取檔/下載/刪除時不問這個檔是不是你的」),所以不單獨討論它的等級——補上那一套歸屬檢查,四條一起消掉。風險等級一律以工單上的登記為準。
原本評中的依據是「這是破壞不是竊取」。這個依據對一般系統成立,對稽核產品不成立:
| 證據被取走 | 難看,但稽核還做得下去 |
| 證據被刪除 | 稽核做不下去了,而且不可逆——沒有救回的機制 |
而且查證時發現:刪除的防護比下載還薄。 下載與預覽至少接受兩條路(登入憑證或短效票),刪除只驗登入,連票那條路都沒接上,業務層也沒有任何判斷。
伺服器不會執行任何上傳上來的檔案。 整套件查證過,唯一會叫外部程式的地方是把文件轉成 PDF 的那一步,而且參數是寫死的,塞不進別的東西。
這條講的是另一件事:別人點開預覽時,攻擊者的腳本在「他的」瀏覽器裡、用「他的」身分跑起來——可以偷走他的登入狀態、冒用他的身分操作系統功能。
| 出口 | 做法 | 結果 |
|---|---|---|
| 下載 | 一律當附件丟出去 | ✅ 安全 |
| 預覽 | 照使用者自己填的副檔名決定格式,而且允許瀏覽器直接把它畫出來 | ⚠️ 有問題 |
同一個模組、同一份檔案,兩條路做法不一樣。 這說明不是「不知道要做」,是預覽那一條漏掉了。
要求是:HTML 報告要能繼續預覽。 所以不能用「乾脆全部不給預覽」這種解法。
定案的做法是三件事一起做:
| 做什麼 | 解決什麼 | |
|---|---|---|
| 一 | 預覽端建一份可預覽的類型清單,清單外一律當附件下載 | 把可預覽的範圍縮到可控 |
| 二 | 清單內的,回應時加上瀏覽器層的隔離指令——瀏覽器會禁止這個頁面執行腳本、也不讓它碰我們網域的任何東西,排版與圖表照常看得到 | 就算清單內混進惡意內容也跑不起來 |
| 三 | 不只看副檔名,由伺服器自己確認檔案內容真的是那個格式 | 目前副檔名是直接拿使用者上傳的檔名,完全沒有驗證 |
被排除的兩個選項,以及理由:
| 選項 | 為什麼不採 |
|---|---|
| 轉成 PDF 再預覽 | HTML 轉 PDF 常跑版、每次預覽都要等轉檔,而且只解決 HTML,不涵蓋其他格式 |
| 把上傳的檔案放到另一個網域 | 落地版是客戶各自裝機,多一個網域就是裝機文件上多一條——某個客戶漏做就整個失效 |
隔離指令之所以是首選:一行就設定完、涵蓋所有類型(包含以後才冒出來的新格式)、不需要額外建設任何東西,而且是瀏覽器本來就支援的標準做法。
這條的特別之處:它讓其他所有修補都失去意義。
拿到雲端儲存的帳號密碼之後,可以完全繞過我們的系統、直接連進儲存空間——那時候不管應用程式裡補了多少檢查,都沒有用。
三層防護同時失效:
| 第幾層 | 本來應該做什麼 | 實際狀況 |
|---|---|---|
| 一 | 用來遮蓋機密的名單,要收錄儲存設定 | 沒有收錄 |
| 二 | 要遮蓋的欄位名清單,要包含帳密欄位 | 沒有包含實際用的欄位名 |
| 三 | 查詢設定的功能要檢查權限 | 只檢查有沒有登入 |
這一條的證據等級要標清楚:是從程式碼推論成立,沒有現場實測過
開發環境還沒有設定雲端儲存,那張設定表是空的,在本機打不出症狀。這跟客戶資料隔離那一條(實際撈到筆數、任何人都能重跑)是不同等級的證據。
在稽核場合,「我們推論成立但還沒實測」是可以接受的說法;「被發現沒標」才不可接受。
影響範圍也要說清楚:那張設定表本身有做客戶隔離,所以拿到的是自己那家公司的帳密,不是全部客戶的。但落地版是一客戶一套系統,那家就是全部——對主力出貨形態沒有減輕。
原則比原本的建議更強:設定類的密碼欄位一律不回傳給前端,只有程式內部要用的時候才取得真值。
| 做法 | 漏掉的時候會怎樣 | |
|---|---|---|
| 原本的建議 | 把儲存設定補進「要遮罩」的名單 | 漏登記 = 密碼外洩。這次就是這樣發生的——兩份名單都漏了儲存設定 |
| 定案的做法 | 反過來:預設全部蓋掉,明確標記可以公開的才給 | 漏標記 = 頂多前端少看到一個欄位,前端會自己來反應 |
差別在於漏掉的時候往哪一邊倒。這個定法也順帶取消了「要不要盤查其他設定組有沒有漏登記」這個工作——預設就是蓋的,不用盤。
所有修法都要先確定「功能不會壞」。這一條的順序如果做錯,會把客戶的密碼洗掉。
| 步驟 | 做什麼 | 為什麼不能往後排 |
|---|---|---|
| 一 | 後端先加防護網:收到空值就保留原值、不要覆蓋 | 這一步必須第一個做。有了它,後面任何一步出錯都不會造成密碼被清空 |
| 二 | 前端改成遮蔽顯示(••••••••),不填就保持原值 |
跳過這步直接做第三步的話,使用者改別的欄位順手存檔,就會把密碼存成空的 |
| 三 | 後端改成不回傳密碼 | 前面兩道都到位了才動這裡 |
| 四 | 先查清楚有沒有背景工作在讀這組設定(匯入、排程、轉檔常常用系統身分跑),確認後再補權限檢查 | 不查就補,可能把它們擋掉——而且會悄悄失敗,不會有人看到錯誤 |
| 五 | 盤點那組帳密還寫死在哪些地方(環境變數、其他服務的設定檔、部署腳本),把它們收攏成單一來源 | 同一組帳密散落在很多處,日後真要換的時候必然漏掉一處,而症狀是「某個功能突然壞了」、不是明顯的錯誤訊息。趁出貨前收乾淨,成本最低 |
這一步不包含「換發密碼」,原因要講清楚
先前這一步寫的是「盤點完之後換發那組帳密」,前提是那組帳密已經外流。查證後這個前提不成立:
但盤點本身仍然要做,理由換成另一個:帳密不該寫死在程式與腳本裡。這是出貨前該收乾淨的衛生問題,不是外洩應變。
要講清楚的是:這不表示第 5 條不嚴重。那個漏洞是真的——只要產品一出貨、客戶一填進自己的儲存帳密,任何有帳號的人就拿得到。差別只在於目前還沒有真實客戶受害,所以現在修的成本最低。
這條先前寫成「要先做產品決策:客戶之間共用儲存空間是不是刻意的設計」——那個判斷是錯的。
查證結果:它不是產品決策題。它是 2026-07-29 修另一個錯誤(背景工作把檔案存錯客戶)時加上的讀取端過渡措施,當時的紀錄原話是「搬家完成前與未來零星錯位的讀取保底」。
那次已經把寫入端治本了(背景工作改成用該工作所屬的客戶去解析),這一段只是讓已經存錯位置的舊檔還讀得到。搬家從來沒做,所以它留到現在。
系統層的設定確實存在(有一列是給系統用的)。但正規取系統預設的程式都會明確指定要那一列,只有這一段沒有任何條件——它是把全部撈出來、照編號排序取第一筆。
實際資料上,系統那一列的編號排在業務客戶後面,所以它永遠撈不到系統那列。實際借到的是最早建立的那家客戶的儲存空間。
| 情況 | |
|---|---|
| ① | 客戶完全沒有儲存設定——目前開發環境裡有 3 個測試客戶是這樣(都是我們自己建的測試資料,不是真實客戶) |
| ② | 客戶換過儲存後端,舊檔還記著舊的型別 |
| ③ | 當初那批存錯位置的舊檔 |
一個典型的「驗收環境打折掩蓋缺口」
開發環境的那幾筆儲存設定,位址、空間名、帳號完全相同——所以借到誰的都看不出差別,測起來一切正常。
要等客戶各自接上自己的儲存空間,這個問題才會現形。
| 做什麼 | 效果 | |
|---|---|---|
| 先做 | 把「撈全部取第一筆」改成「只取系統層那一列」 | 一行改動,立刻從「借別家客戶的設定」變成「用系統預設」,風險當場消失 |
| 再做 | 記錄成已知的過渡措施,等錯位檔搬家完成後整段拿掉 | 治本 |
被排除的選項:先把搬家做完再拿掉這條退路——那是治本沒錯,但卡在搬家工具還沒做(工單 CM-1279),等它等不到。
另外兩件順手要做的:
| 補三個測試客戶的設定 | 開發環境有三個測試客戶完全沒有儲存設定,原因是自動複製的機制(CM-1282)只對之後新建的客戶生效,先前建的沒有回頭補。順手補一次即可 |
| 把那行警告升級 | 目前出事時只有一行警告混在一般日誌裡,沒有稽核事件、沒有告警。對稽核產品來說,「這個檔案到底存在誰的空間」是必須答得出來的問題 |
先前寫「設定沒特別填的話會走明文」——這個措辭不準確,而且差別很重要。
實際查開發環境的資料庫:七筆儲存設定全部都填了,而且全部填成不加密。
| 先前的寫法 | 實際狀況 |
|---|---|
| 大家忘了填(疏忽) | 目前的設定就是不加密(現況) |
差別在修法:不能只改預設值——現有環境都已經明確填了「不加密」,改預設對它們完全無效。
這條講的是我們自己裝在客戶機房的物件儲存(MinIO/SeaweedFS)那條連線。
Google Drive 走的是 Google 官方工具,底層固定加密,連「要不要加密」這個開關都沒有。目前的寫法很容易讓人誤會成 Drive 那邊也有問題。
| 出貨裝機時 | 預設開啟加密 |
| 現有環境 | 另外評估切換時機(要確認兩端都支援、切換當下會不會斷線) |
這些本頁不下結論,列出來等決策。
| 待決的事 | 目前手上的判斷依據 | |
|---|---|---|
| 一 | 第 5 條在全案的優先順序 | 分析上建議排在第 1、2、3、7 條之前或同一批——理由是它修起來最便宜,而且不修的話前面那幾條的修補可以被繞過(拿到儲存帳密的人根本不走我們的門) |
| 二 | 「系統共用」檔案的定義與授權規則 | 目前標成系統共用的那些檔在隔離規則裡是跨客戶全開的,開發環境裡有 14 筆這樣的資料(都是測試資料)。要定的是規則本身:「哪些檔案有資格標成系統共用、誰能標」——出貨前定下來,才不會讓真實客戶的檔案落進這個狀態。待決策者與 PM |
| 三 | 檔案數量上限訂多少 | 這是業務問題——證據檔平常多大、有沒有客戶會傳影片或整包日誌。待決策者與 PM |
原本列在這裡待決、2026-09-20 已裁定的兩件,記錄在此供稽核對照——不是憑空消失。
一句話:這是高風險問題最多的一塊,而且它守的是稽核證據——證據能被取走、竄改或刪除,整個產品的價值就受影響。
建議:
另外要提醒:第 6 條雖然標示已修,但它是別的功能開發順手做的,不是針對這個問題修的——那項功能要拿這張表當暫存區才需要加欄位。這一個月補起來的全是資料庫那一層,應用程式那一層一條都沒動過。剩下三道防線都還開著,不要因為看到「已修」就認為這塊沒事了。
最後要講清楚一件事:產品目前還沒有出貨給任何真實客戶,所以上面每一條都還沒有傷害到任何人——現在看到的檔案與設定都是我們自己的測試資料。但這不代表問題不嚴重。 每一條都是真的,產品一上線就會是真實客戶的稽核證據在承擔。這一輪檢視的目的,正是在那之前把它們處理掉——現在修,是最便宜的時候。
| 統計 | |
|---|---|
| 檢視輪數 | 3 輪、71 個檔案 |
| 原本列出 | 11 條 |
| 查證後成立 | 10 條(1 條查證後不成立,見上方說明) |
| 高風險 | 7 條 |
| 中風險 | 2 條 |
| 低風險 | 1 條 |
| 已修 | 1 條(資料庫層) |
| 部分修 | 2 條 |
| 未修 | 7 條 |
| 仍待決策 | 3 件(見上方) |
| 主系統掃描另查出 | 1 條(第 12 條,中風險,部分修) |