L1 檢查結果:日誌轉送全鏈(jedi-log)

L1 檢查結果:日誌轉送全鏈(jedi-log)

檢查日期 2026-09-15|耗時 1 小時 29 分|對應卡片 CM-1811|檢查範圍 39 個檔案


§1

🔴 一句話結論

兩條新問題,都是中風險:一是「日誌送到外部伺服器的路上完全沒有加密」,二是「有心人可以在日誌裡塞偽造的假紀錄」。都不是「誰能改設定」這類權限漏洞,而是「送出去的資料本身」不夠安全。


§2

這一棒在檢查什麼

系統可以把自己產生的日誌即時送到外部伺服器(客戶自己的資安監控系統,即 SIEM)。這一棒檢查的就是這條完整路徑的 39 個檔案:從「設定頁的三支 API」一路到「實際送出網路封包的背景程式」,再到「隨套件出貨的兩支資料庫腳本」。

要回答的核心問題是:誰改得動「日誌要送去哪裡」,以及送出去的東西夠不夠安全。


§3

掃到什麼:總覽表

# 嚴重度 這是什麼問題 出事會怎樣 要先有什麼才打得到 在哪裡 狀態
F1 🟡 中 日誌送到外部伺服器的整條路上完全沒有加密選項 只要有人能在網路傳輸路徑上偷看封包(例如同一個機房網段、或被入侵的交換器),就能原封不動看到整份轉送出去的日誌內容,包含操作者身分與其他敏感資料 ① 管理員已把日誌轉送功能設定好、指向真實的伺服器
② 攻擊者要能站在「應用程式主機」到「日誌伺服器」這條網路路徑上
jedi_api_log/forwarding/common/forwarder.py:181 🆕 新增
F2 🟡 中 送出去的日誌內容沒有過濾換行符號,可以被塞入偽造紀錄 有心人可以在系統會記錄下來的欄位(例如登入帳號)裡藏換行符號加一段偽造內容,這段偽造內容會被當成獨立一筆「真的」日誌送到客戶的 SIEM,等於能捏造假的稽核紀錄 ① 日誌轉送功能已啟用(系統預設就是啟用的)且協定選 syslog
② 有一個使用者能碰到的欄位,其值會被原封不動寫進日誌
jedi_api_log/forwarding/common/forwarder.py:232 🆕 新增

淨結果:工具提報 2 條候選,全數通過驗證,都是新發現,都是中風險。


§4

每條發現的詳述

F1:日誌轉送沒有加密選項

現況:已修(M09-3,CM-2056,1.21.0 出貨)

①這是什麼問題:日誌轉送功能不管選哪個通訊協定(syslog 或 gelf)、走 UDP 還是 TCP,全部走「不加密」的傳輸方式,套件裡完全沒有任何加密相關的程式碼。

②出事會怎樣:轉送出去的內容包含應用程式日誌與稽核事件——操作者身分、事件細節,以及其他業務資料可能夾帶的內容。跨 arc 總表已登記的一項待辦(BE 日誌會原文記下請求內容,包含密碼)意味著這些明文密碼也有機會經由這條沒加密的管道被送出去。任何能在網路傳輸路徑上偷看封包的人,都能原封不動看到這些內容。

③要先有什麼才打得到:

  • 管理員已經把日誌轉送功能設定好、指向一台真實的伺服器(這是管理員的操作,不是預設狀態)
  • 攻擊者要能站在「應用程式主機」到「客戶指定的日誌伺服器」這段網路路徑上(例如同一個機房網段、被入侵的交換器、或攔截 WAN 線路的設備)——這套系統主要是落地部署給客戶,這段路徑很可能跨出單一信任邊界

④在哪裡: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 票通過,可信度標為「中」而非「高」,但這不影響它是真實存在的缺口——沒有加密選項本身就是問題,不管是誰設定的目的地。


F2:日誌轉送內容可被塞入偽造紀錄(日誌注入)

現況:已修(M09-4,CM-2055,1.21.0 出貨)

①這是什麼問題:組出要送出去的 syslog 格式訊息時,程式沒有過濾掉訊息內容裡的換行符號。

②出事會怎樣:如果攻擊者能讓某個值(例如登入帳號欄位)被寫進會被轉送的日誌裡,而這個值裡藏了換行符號加一段偽造的內容,這段偽造內容送到客戶的 SIEM 之後,很多解析器會把這個換行符號當成「這是新的一筆紀錄」,於是偽造內容就變成一筆「看起來是真的」的獨立日誌,等於能捏造假的操作紀錄、混淆事後追查。

③要先有什麼才打得到:

  • 日誌轉送功能要是啟用狀態,而且協定要選 syslog、有設定好的主機與埠號(系統預設就是啟用的,不需要額外開啟)
  • 要有一個使用者能碰到的欄位,它的值會被原封不動記進日誌裡(例如登入時輸入的帳號)

④在哪裡:jedi_api_log/forwarding/common/forwarder.py:232(_Rfc5424Formatter.format 函式,組出最終要送出去的那一行文字)

⑤怎麼修:在組出 syslog 那一行文字之前,把訊息內容裡的換行符號(\r、\n)等控制字元過濾或替換掉,做法可以參考同套件裡 gelf 傳輸方式的處理——它已經把不可控的文字限制在第一行。

首腦補充(驗證細節):三位檢查員一致判定成立(3 票全過),其中一位親自用測試用的 Flask 客戶端送出帶編碼換行符號的請求,實際驗證了換行符號能一路傳到轉送格式化那一行——這是本棒唯一一條有實際操作驗證(非僅讀程式碼推論)的發現,可信度標為「高」。


§5

已知、非本棒新發現(照卡片交代核對)

卡片開卡前已經查證過三件事,這一棒掃到的內容與這三件一致,不重複計數:

  1. 三支轉送端點在套件裡沒掛任何權限裝飾器——這是刻意設計(守門由主專案宿主注入),已經追到 core/plugins/api_log.py:185-186 確實有掛上「要登入+要有對應權限」,讀跟寫是兩個不同的權限。這次掃描沒有誤報成「無守門」。
  2. 轉送設定表沒有做客戶隔離——建表腳本檔頭已寫明理由(平台層設定、背景執行緒沒有請求脈絡)。這次掃描沒有把它當成漏洞誤報。
  3. 設定表裡沒有帳號密碼欄位——這次逐欄核對過建表腳本,確認沒有帳密欄位,跨 arc 總表先前那句「轉送設定裡包含帳密」的說法已經是錯的、不需要再更正一次。

§6

這份結果可信到什麼程度

「這兩條是真的嗎」→ ✅ 可信

  • 兩條發現都通過三位獨立檢查員投票:F1 是 2 票對 1 票通過,F2 是 3 票全過。
  • F2 有一位檢查員實際打過測試請求驗證換行符號真的能傳到底,不是只憑讀程式碼推論。
  • F1 的疑慮不是「這條不成立」,而是「誰能觸發它」這件事上有一票保留——已如實反映在信心等級上,不影響發現本身存在。

「是不是只有這兩條」→ ❌ 不完全可信,理由如下

這是最低強度快篩,不是全面檢查。 用的是 low 強度:一位研究員把 39 個檔案讀完就提報候選,不做元件盤點、不做威脅建模、不跑額外的密鑰專項掃描(雖然如此,低強度仍內建一次密鑰檢查,未在本次掃到已知密鑰洩漏)。所有結論都來自「讀程式碼」,唯一的例外是 F2 的那一次實測請求;其餘沒有實際打過 API、沒有做過完整的攻擊鏈驗證。

卡片上列了六個重點追查方向(本棒最重要的一條是「客戶層級權限改到全系統共用設定」),本次掃描的兩條發現落在「加密」與「注入」這兩個方向,卡片重點①(客戶管理員權限改到全域設定)沒有被工具直接提出成候選——這不代表那個疑點不成立,只代表這次的研究員視角沒有把它獨立寫成一條發現。下一棒接手或後續複核時,這一點仍待另外確認。


§7

執行概況

項目 數字
檢查範圍 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)依工具原始產物撰寫,非首腦補寫。