檢查日期 2026-09-15|耗時 1 小時 29 分|對應卡片 CM-1811|檢查範圍 39 個檔案
兩條新問題,都是中風險:一是「日誌送到外部伺服器的路上完全沒有加密」,二是「有心人可以在日誌裡塞偽造的假紀錄」。都不是「誰能改設定」這類權限漏洞,而是「送出去的資料本身」不夠安全。
系統可以把自己產生的日誌即時送到外部伺服器(客戶自己的資安監控系統,即 SIEM)。這一棒檢查的就是這條完整路徑的 39 個檔案:從「設定頁的三支 API」一路到「實際送出網路封包的背景程式」,再到「隨套件出貨的兩支資料庫腳本」。
要回答的核心問題是:誰改得動「日誌要送去哪裡」,以及送出去的東西夠不夠安全。
| # | 嚴重度 | 這是什麼問題 | 出事會怎樣 | 要先有什麼才打得到 | 在哪裡 | 狀態 |
|---|---|---|---|---|---|---|
| F1 | 🟡 中 | 日誌送到外部伺服器的整條路上完全沒有加密選項 | 只要有人能在網路傳輸路徑上偷看封包(例如同一個機房網段、或被入侵的交換器),就能原封不動看到整份轉送出去的日誌內容,包含操作者身分與其他敏感資料 | ① 管理員已把日誌轉送功能設定好、指向真實的伺服器 ② 攻擊者要能站在「應用程式主機」到「日誌伺服器」這條網路路徑上 |
jedi_api_log/forwarding/common/forwarder.py:181 |
🆕 新增 |
| F2 | 🟡 中 | 送出去的日誌內容沒有過濾換行符號,可以被塞入偽造紀錄 | 有心人可以在系統會記錄下來的欄位(例如登入帳號)裡藏換行符號加一段偽造內容,這段偽造內容會被當成獨立一筆「真的」日誌送到客戶的 SIEM,等於能捏造假的稽核紀錄 | ① 日誌轉送功能已啟用(系統預設就是啟用的)且協定選 syslog ② 有一個使用者能碰到的欄位,其值會被原封不動寫進日誌 |
jedi_api_log/forwarding/common/forwarder.py:232 |
🆕 新增 |
淨結果:工具提報 2 條候選,全數通過驗證,都是新發現,都是中風險。
現況:已修(M09-3,CM-2056,1.21.0 出貨)
①這是什麼問題:日誌轉送功能不管選哪個通訊協定(syslog 或 gelf)、走 UDP 還是 TCP,全部走「不加密」的傳輸方式,套件裡完全沒有任何加密相關的程式碼。
②出事會怎樣:轉送出去的內容包含應用程式日誌與稽核事件——操作者身分、事件細節,以及其他業務資料可能夾帶的內容。跨 arc 總表已登記的一項待辦(BE 日誌會原文記下請求內容,包含密碼)意味著這些明文密碼也有機會經由這條沒加密的管道被送出去。任何能在網路傳輸路徑上偷看封包的人,都能原封不動看到這些內容。
③要先有什麼才打得到:
④在哪裡:jedi_api_log/forwarding/common/forwarder.py:181(build_target_handler 函式,組出 syslog 的 handler),同樣的問題也存在於 common/gelf_handler.py 的 gelf 傳輸實作。
⑤怎麼修:幫兩種傳輸協定都加上可選的 TLS 傳輸方式(syslog 走 RFC 5425 TLS syslog,或至少用 ssl.wrap_socket/SSLContext 包一層 TCP+TLS),並且要把這個選項開放在設定 API 的 transport 欄位上(目前只能選 udp/tcp)。
首腦補充(驗證細節):三位檢查員中有一位對「這條打不打得到」持保留意見,理由是「誰改得動轉送目的地」是管理員層級的操作,不是攻擊者能直接觸發的。另外兩位(影響面、有沒有防護)都判定成立。所以最終是 2 票對 1 票通過,可信度標為「中」而非「高」,但這不影響它是真實存在的缺口——沒有加密選項本身就是問題,不管是誰設定的目的地。
現況:已修(M09-4,CM-2055,1.21.0 出貨)
①這是什麼問題:組出要送出去的 syslog 格式訊息時,程式沒有過濾掉訊息內容裡的換行符號。
②出事會怎樣:如果攻擊者能讓某個值(例如登入帳號欄位)被寫進會被轉送的日誌裡,而這個值裡藏了換行符號加一段偽造的內容,這段偽造內容送到客戶的 SIEM 之後,很多解析器會把這個換行符號當成「這是新的一筆紀錄」,於是偽造內容就變成一筆「看起來是真的」的獨立日誌,等於能捏造假的操作紀錄、混淆事後追查。
③要先有什麼才打得到:
④在哪裡:jedi_api_log/forwarding/common/forwarder.py:232(_Rfc5424Formatter.format 函式,組出最終要送出去的那一行文字)
⑤怎麼修:在組出 syslog 那一行文字之前,把訊息內容裡的換行符號(\r、\n)等控制字元過濾或替換掉,做法可以參考同套件裡 gelf 傳輸方式的處理——它已經把不可控的文字限制在第一行。
首腦補充(驗證細節):三位檢查員一致判定成立(3 票全過),其中一位親自用測試用的 Flask 客戶端送出帶編碼換行符號的請求,實際驗證了換行符號能一路傳到轉送格式化那一行——這是本棒唯一一條有實際操作驗證(非僅讀程式碼推論)的發現,可信度標為「高」。
卡片開卡前已經查證過三件事,這一棒掃到的內容與這三件一致,不重複計數:
core/plugins/api_log.py:185-186 確實有掛上「要登入+要有對應權限」,讀跟寫是兩個不同的權限。這次掃描沒有誤報成「無守門」。這是最低強度快篩,不是全面檢查。 用的是 low 強度:一位研究員把 39 個檔案讀完就提報候選,不做元件盤點、不做威脅建模、不跑額外的密鑰專項掃描(雖然如此,低強度仍內建一次密鑰檢查,未在本次掃到已知密鑰洩漏)。所有結論都來自「讀程式碼」,唯一的例外是 F2 的那一次實測請求;其餘沒有實際打過 API、沒有做過完整的攻擊鏈驗證。
卡片上列了六個重點追查方向(本棒最重要的一條是「客戶層級權限改到全系統共用設定」),本次掃描的兩條發現落在「加密」與「注入」這兩個方向,卡片重點①(客戶管理員權限改到全域設定)沒有被工具直接提出成候選——這不代表那個疑點不成立,只代表這次的研究員視角沒有把它獨立寫成一條發現。下一棒接手或後續複核時,這一點仍待另外確認。
| 項目 | 數字 |
|---|---|
| 檢查範圍 | 39 個檔案(與卡片逐檔對上) |
| 檢查強度 | 最低(low),未設定 focus(範圍已經夠小,全讀) |
| 派出/回報的 agent | 7 / 7(1 位研究員+6 個投票 agent) |
| 候選問題 → 去除重複 | 2 → 2 |
| 投票數 | 6(2 條發現 × 3 位檢查員) |
| F1 投票結果 | 2 票通過 / 1 票不同意(REACHABILITY 持保留) |
| F2 投票結果 | 3 票全過 |
| 沒投到票的 / 投票中斷的 / 被降低嚴重度的 | 0 / 0 / 0 |
| 驗證章狀態 | verified(無拒收理由) |
| 掃描當下的程式碼版本 | commit 9b35885b2723,branch main,工作區乾淨 |
| 耗時 | 1 小時 29 分(5372 秒) |
| 花費 token(子 agent 累計) | 約 113 萬 |
📄 本報告由 runner(本棒 session)依工具原始產物撰寫,非首腦補寫。