---
title: FR-084 T1b 本機實測——換掉驗章函式庫是否真能繞過整套防竄改
status: ✅ 實測完成，T1-2 **確認成立**
notion:
  card: {id: CM-1650, title: "FR-084.T1b 本機實測驗證「換掉驗章函式庫即可繞過整套防竄改」（T1-2，本機唯讀實驗，不碰任何環境）"}
relates: [FR-084]
---

# 🔴 一句話結論

**T1-2 確認成立**：只要能換掉負責「核對簽章」的那個工具（`cryptography` 函式庫），開機檢查、以及之後每四小時一次的抽查，全部會照樣放行一支已經被改過的核心檔案，而且不會留下任何錯誤訊息。原本判 HIGH 的嚴重度**維持不變，建議不降級**。

---

## 這一棒在做什麼（白話）

FR-084 掃描（CM-1645）之前只靠**讀程式碼**推論出一件事：防竄改機制開機時會逐一核對檔案有沒有被改過，但**負責「怎麼核對」的那個工具本身，不在核對範圍內**。這聽起來像是「鎖大門，但鑰匙孔本身沒人看守」，可是這只是讀程式碼得到的推論，還沒有人真的動手試過。

這一棒的任務就是**真的動手做一次**：在自己的電腦上，假裝自己是拿到主機權限的攻擊者，把「核對簽章」這個工具換成一個永遠說「沒問題」的假貨，看看被改過的檔案能不能就這樣蒙混過關。**全程只在暫存目錄（tmp）裡操作，沒有連過任何正式環境，也沒有改過任何一行產品程式碼。**

---

## 結論總覽（三個問題）

| # | 問題 | 結論 | 屬於「實測」還是「推導」 |
|---|---|---|---|
| ① | 驗章工具（`cryptography`）是不是真的落在「開機不核對」的第三方層？ | **是** | 分層邏輯推導 ＋ 套件實際安裝資訊佐證（沒有跑完整正式 build，見下方說明） |
| ② | 掉包驗章工具後，開機檢查是否仍會放行被改過的檔案？ | **會放行**——閘門印出「驗證通過」，正常繼續啟動 | **實測**（真的跑了套件自帶的實驗台） |
| ③ | 每四小時一次的抽查，是不是也一起失效？ | **是**，一起失效，回傳「通過」 | **實測** |

---

## 詳述

### ① `cryptography` 是不是真的在「開機不核對」的第三方層

**是。** 這裡分兩步驗證。

**第一步：開機到底核對哪些層？**
防竄改機制把產品的檔案分成三層——`core`（核心程式）、`resources`（資源檔）、`thirdparty`（別人寫的公開套件，例如 pandas、cryptography 這些）。開機時只核對前兩層，`thirdparty` 層完全不核對，只交給「每四小時一次的抽查」去隨機抽樣覆蓋。

這個設定寫死在套件的設定檔裡：

```
DEFAULT_BOOT_LAYERS = ("core", "resources")
```
（`jedi-integrity/jedi_integrity/config.py:49`，jedi monorepo）

**第二步：`cryptography` 有沒有被歸進不核對的那一層？**
落地版產品用 Nuitka 把大部分程式碼編譯成機器碼，但少數幾個「公開的大套件」為了省編譯時間，選擇**不編譯、原樣照抄一份放進產物**——這份「不編譯的名單」就決定了誰會被歸進 `thirdparty` 層。`cryptography` 本身不在這份名單上，但清單裡的 `pdfminer`（PDF 解析用的套件）**依賴** `cryptography`，而系統會把「被排除套件的所有相依套件」一起連坐排除，所以 `cryptography` 也被一起帶進了不核對的那一層。

實際查證：
```bash
$ .venv/bin/python -c "import importlib.metadata as m; print(m.requires('pdfminer.six'))"
['charset-normalizer>=2.0.0', 'cryptography>=36.0.0', 'Pillow; extra == "image"']
```
確認 `pdfminer.six` 的相依清單裡明確寫了 `cryptography>=36.0.0`。

再用 BE repo `scripts/build/build_release.sh` 裡「求排除套件完整相依閉包」的那段邏輯（`resolve_excluded_closure()`，`:370-406`），在本機用同一套演算法實際跑一次（不是憑空猜測，是把腳本裡那段 Python 邏輯抽出來、餵進本機已安裝的套件清單去跑），輸出的閉包清單裡**確實包含 `cryptography`**：

```
Crypto、PIL、_cffi_backend、apiclient、certifi、cffi、charset_normalizer、
cryptography、dateutil、fontTools、google、...（共 35 項）
```

**這一步屬於「分層邏輯推導 ＋ metadata 佐證」，不是實測**——沒有真的跑一次完整的 15～20 分鐘正式 build 去產出落地版的成品清單再 grep 確認（卡片明說沒有現成產物就不必為此重編）。但邏輯鏈本身是可重現的、每一步都貼了實際指令輸出，可信度高。

### ② 掉包驗章工具後，開機檢查是否還會放行被改過的檔案

**會，完全放行。這一步是實測，不是推論。**

**怎麼測的**：套件自己附了一個「實驗台」（`jedi-integrity/harness/`），可以在不碰任何真實產品的情況下，在暫存目錄裡造一份假的「產品檔案」＋一份真的數位簽章，然後真的跑一次「開機檢查」的程式碼。這個實驗台本來就是套件作者留給開發者驗證用的，我們用它，沒有另外碰觸真實產品的簽章或憑證。

**基準線（確認機制本來是有效的）**：先跑「改一支核心檔案、不動驗章工具」的情境，確認開機檢查**真的會擋下來**：
```
[FATAL] 啟動失敗：產物完整性驗證未通過，服務拒絕啟動並已鎖定（視同無有效授權）。
  原因：檔案內容與簽章 manifest 不符
  不符檔案（共 1 項）：
    - [core] common/integrity/startup_gate.so（預期 d32aad21d655… 實際 51689e1aaa53…）
```
→ 機制本身沒有壞，正常情況下確實能擋下竄改。

**關鍵實驗**：接著寫了一支「惡意驗章器」——這不是改套件的程式碼，是另外寫一支獨立的小工具，去**冒充**套件原本要用的那個驗章元件（這正是攻擊者真正會做的事：把系統呼叫的驗章工具整支換掉，而不是去改讀不懂的簽章演算法本身）。這支假驗章器做兩件事：
1. **完全不檢查簽章對不對**（正常的驗章器會用密碼學方式確認「這份清單真的是原廠簽的」，假驗章器直接跳過這一步）；
2. **順手把清單裡「這支檔案應該長什麼樣子」的紀錄，改成被竄改後的檔案現在真正的樣子**——因為驗章器是整條鏈唯一負責告訴系統「這份清單可以相信」的角色，呼叫方（開機檢查本身）完全信任它回傳的結果，不會再做任何獨立複查。這一步就是把「掉包驗章工具」的攻擊完整模擬出來：攻擊者能執行任意程式碼，讓假驗章器順手算出竄改後檔案的新雜湊值不需要額外門檻。

再重跑一次「改一支核心檔案 + 用這支假驗章器」的情境，結果：

```
[poc] 已竄改核心檔：common/integrity/startup_gate.so
[poc] 情境 A：掉包驗章器 + 未重簽 manifest → 跑 run_startup_gate()
[INTEGRITY] 產物完整性驗證通過（core+resources 共 4 檔，version=harness-0.0.1）
[poc] 結果：閘門【通過】，儘管檔案已被竄改、manifest 未重簽。
```

**開機檢查印出「驗證通過」，正常放行，跟檔案完全沒被動過的情況看起來一模一樣，沒有任何警告或異常訊號。**

### ③ 每四小時一次的抽查，是不是也一起失效

**是，一起失效。也是實測。**

抽查程式碼（`run_spot_check`）跟開機檢查共用同一段「讀取並確認簽章清單」的邏輯（`jedi_integrity/manifest.py` 的 `load_verified_manifest`），也就是**同一個驗章器**。接續上面的實驗，開機檢查通過後緊接著跑一次抽查：

```
[poc] 情境 B：閘門通過後接線 runtime checker，跑一次抽查（spot check）
[poc] 抽查回傳值：True（True＝通過／視為完整）
```

抽查回傳 `True`（代表「檢查完整，沒發現問題」），跟正常情況一模一樣。**代表這套機制唯一的「事後補救」——四小時抽查一次——同樣會被同一次掉包一併打穿，不會因為抽查多跑一次就抓到破綻。**

---

## 這份結果可信到什麼程度

- **② 和 ③ 是真的跑程式碼得到的結果**，不是猜測——用的是套件作者自己附的官方實驗台，跑的是正式的 `run_startup_gate()` 與 `run_spot_check()` 函式本身，沒有走任何捷徑或模擬殼。
- **① 是邏輯推導**，鏈條完整（分層設定寫死在設定檔 → 排除清單邏輯用本機真實已安裝套件跑過一次 → pdfminer 對 cryptography 的相依關係也實際查證過），但**沒有跑過一次完整的正式 build 去產出真正落地版的成品清單**。若要把這一步也變成完全的實測，需要另外花 15～20 分鐘在有資源的機器上跑一次完整 build，卡片本身也說明「沒有現成產物就不必為此重編」。
- **只驗了這一個攻擊路徑**，沒有測試「如果攻擊者用別的方式偽造簽章」（例如直接猜出私鑰、或找演算法本身的漏洞）——那些不是這一棒的範圍，機制的密碼學演算法本身（Ed25519）不在懷疑之列。

---

## 建議

T1-2 **確認成立**，建議跨 arc 總表 §3.1 第 13～18 項裡對應這一條的嚴重度**維持 HIGH**，不降級。理由：這不只是「理論上可能」，是實測證實「掉包一個工具，整套保護（開機檢查＋定期抽查）同時、完全失效，且系統自己毫無察覺、不會留下任何錯誤或警示」。是否要開修正卡、修法方向為何，交由決策者在後續裁定。

---

## 附錄：實驗環境與操作紀錄

- **branch**：jedi monorepo 與 BE repo 皆為 `feature/FR-075`，兩者皆未切換。
- **執行位置**：全程在本機 `poetry` 虛擬環境內操作，PoC 腳本與所有產出檔案只存在於系統暫存目錄（`/private/tmp/...`），未寫入任何 repo 目錄，實驗結束後已由套件既有的 `cleanup()` 機制清除暫存目錄。
- **修改範圍**：**沒有修改 `jedi-integrity` 套件任何一行原始碼**，`harness/dev_app.py` 原檔未動。惡意驗章器是另開的一支獨立 PoC 腳本（只在暫存目錄存在，未 commit 進任何 repo），透過 import 套件既有的公開介面（`ISignatureVerifier` port）注入，屬正常的「換一顆符合介面的元件」用法，不是修改套件內部。
- **密鑰處理**：實驗用的 Ed25519 金鑰對是套件自帶實驗台每次執行時現場產生的假鑰（僅供這次跑的暫存環境使用），本報告未記錄任何金鑰或簽章的實際數值，只記錄雜湊值的前綴（既有機制本身的訊息格式，非本次額外揭露）。
- **環境影響**：全程唯讀＋本機沙盒操作，未連線 DEV／STG／POC 任一環境，未觸碰 `/opt/guidant/pki`，未部署或重啟任何服務。
