安全設計依據
本頁保存那些程式碼無法呈現其理由的安全規則: 針對每一條信任邊界, 列出規則、它所拒絕的設計, 以及原因。本頁不描述機制, 也不描述殘餘風險; 兩者都由信任邊界帳冊負責。只有在未來的變更有可能重新引入被拒絕的設計時, 才會有對應的一列。
用戶端程式准入與用戶端允許清單
| 規則 | 被拒絕的設計 | 原因 |
|---|---|---|
| 准入以經過證明的錨點 (程式碼簽署者或映像雜湊) 為鍵; 用戶端名稱只是日誌標籤 | 依自我宣稱的用戶端名稱 (GENKAN_CLIENT_NAME) 准入 | 名稱只是任何程序都能放進環境變數的字串; 錨點則是核心與 Security 框架對執行中映像所做的證言 |
已簽署的用戶端程式 (harness) 以明確的 pair-client --signer 錨點配對 (--this-parent 一律固定映像雜湊); 雜湊錨點用於未簽署或 ad-hoc 建置, 以及 Linux | 對每個用戶端都固定確切的映像雜湊 | 免費的 Apple Development 憑證大約每週重新簽署一次, 每次都會改變 cdhash, 所以雜湊錨點需要每週重新配對; 每週嘮叨一次的控制措施會被停用 |
| 第一個啟動伺服器的程序永遠不會被自動登記 | 首次使用即信任 | 無聲的首次使用授權會把位置交給搶先的那個程序; 登記是使用者的行為 |
尚未配對任何用戶端 (trust.json 中 "clients": null, 或根本沒有記錄) 表示不強制准入, 每次啟動都以 ERROR 等級記錄; 損壞或無法讀取的記錄則拒絕所有人 | 把損壞的檔案讀作「未登記」 | 把載入失敗讀作未登記就是失敗即開放 |
| 撤銷最後一個用戶端後留下一份誰也不准入的空清單 | 最後一個條目移除時把清單重設為 null | null 是首次安裝的起始狀態; 「使用者撤銷了每一個用戶端」必須讀作鎖定, 絕不能讀作重設 |
主機身分與證明
| 規則 | 被拒絕的設計 | 原因 |
|---|---|---|
| 在編譯進證明功能的地方, 橋接對端只有在執行與接受方相同的執行檔時才被接受; 各作業系統的狀態見帳冊中的表格 | 一份可透過環境變數設定的受信任映像雜湊清單 | 同一使用者的程序能透過環境設定的清單, 讓它得以附加其冒充者的雜湊; 「與我相同的執行檔」在構造上就無法偽造, 也不需要任何設定 |
| 自身身分在啟動時量測, 早於 bind、accept 或 dial | 在首次使用時才延遲量測自身 | 之後替換執行檔檔案無法重新定義「自身」, 進而被當作相符的對端接受 |
撤銷與緊急開關
| 規則 | 被拒絕的設計 | 原因 |
|---|---|---|
| 每個請求都從重新讀取的信任記錄重新決定准入; 分範圍的紀元標記只是給監看者的變更通知, 只比較是否相等, 絕不比較先後 | 只在計數器前進時才重新決定 | 竄改者可以寫入任何值, 而一次未能持久化的遞增會讓計數器過時, 所以以計數器為閘門的決定可能為已撤銷的用戶端提供服務; 直接從記錄本身做出的決定則不會 |
| 在緊急開關 (kill switch) 啟用狀態下啟動的主機會以控制平面模式維持運作 | 拒絕瀏覽器附加並讓主機結束 | 擴充功能每兩秒重新啟動一次主機, 所以一次 kill 會變成只能靠 CLI 脫身的當機迴圈; 控制平面模式讓狀態查詢與啟用操作維持可達 |
| 擴充功能的 kill 鏡像在記錄不存在時允許, 在記錄格式錯誤時拒絕 | 把不存在的鏡像視為已 kill | 全新安裝從未收到任何主機的訊息, 而主機無論如何都會強制執行; 本該是記錄的地方出現垃圾資料, 就是有人寫入過的證據 |
登記與使用者在場
| 規則 | 被拒絕的設計 | 原因 |
|---|---|---|
授予或恢復能力的 CLI 儀式 (pair、pair-client、unkill、policy set) 在任何提示之前先拒絕非終端機的 stdin, 其在場證明是在該終端機上輸入的一句話 | 先提示, 再檢查終端機; 或接受來自任何 stdin 的那句話 | 背景指令碼不得能在使用者面前閃出一個無從解釋的提示; echo release | genkan unkill 無法重新開啟橋接 |
主機金鑰是一把軟體 P-256 金鑰, 存放在作業系統憑證儲存區中 (keyring: 鑰匙圈、認證管理員、Secret Service), 或存放在使用者以 pair --file-store 選擇的檔案中, 在 Unix 上僅擁有者可讀, 在 Windows 上受執行階段目錄的 ACL 保護 | 一把只在 macOS 上可用、每次使用都要求該系統生物辨識提示的硬體繫結金鑰 | 逐次使用的在場來自瀏覽器的 WebAuthn 認證器, 在每個平台上都如此, 這讓主機金鑰只剩一項工作, 即向擴充功能識別這次安裝, 而軟體金鑰在任何地方都做得到; 硬體金鑰把在場綁死在 macOS 上, 讓 Linux 與 Windows 沒有授予通道。同一使用者可讀是接受的收窄 |
--file-store 是明確的選擇, 檔案的存在本身就是這個選擇; 憑證儲存區失敗會被回報, 絕不無聲地改道到檔案 | 儲存區出錯時回退到檔案 | 鎖定或拒絕的儲存區與不存在的儲存區無法區分, 無聲的回退會在使用者不知情下把金鑰搬出儲存區 |
| 擴充功能所請求動作的在場, 是由主機對照它登記的憑證驗證的一次 WebAuthn 斷言; 只有在請求的規則不容許任何已登記憑證時, 擴充功能的視窗才可以擔保 | 讓擴充功能回報是哪個介面滿足了在場 | 主機是強制執行點, 它未經驗證的判定就是瀏覽器那一側任何程式碼都能設定的一個位元; 對已登記的瀏覽器給出視窗回答, 就是在場階梯所禁止的降級 |
| 憑證登記在某一個瀏覽器的標籤之下, 只回答該瀏覽器的請求; 登記另一個瀏覽器需要來自任一已登記憑證的斷言 | 一個憑證池回答所有瀏覽器 | 動作是為發出請求的那條瀏覽器連線執行的, 第二個瀏覽器的認證器替第一個擔保, 會讓一個擁有自己金鑰的設定檔解除針對另一個設定檔啟用的開關; 登記時的跨瀏覽器許可正是讓第二個瀏覽器得以登記的條件 |
| 機器上一個憑證都沒有時的首次登記是首次使用即信任, 在寫入時於信任記錄鎖下重新檢查; 之後的每次登記都需要來自已登記憑證的斷言 | 以 CLI 的終端機確認為首次登記把關; 或從不首次使用即信任 | 全新機器上沒有任何憑證可以擔保, 所以只能二選一: 首次使用, 或終端機下限, 而下限會讓可指令碼化的 pty 成為每個瀏覽器的信任根; 鎖那一側的重新檢查關閉了兩個瀏覽器同時讀到空儲存區的競態 |
| 驗證失敗的斷言被拒絕並稽核, 請求被消耗; 憑證保持登記 | 斷言失敗時取消登記該憑證 | 斷言失敗常常是過期的請求或被取消的提示, 而非攻擊; 簽署計數器已經能抓住計數型認證器的複製品; 失敗即取消登記會讓任何能送出一個壞訊框的人移除該瀏覽器唯一的硬體路徑, 把它降級到視窗 |
WebAuthn 驗證器只接受 attestation: "none"、只接受 ES256 (COSE alg -7, P-256), 並在任一方有計數時拒絕 signCount 未前進的斷言 | 解析各種證明格式並信任認證器的憑證鏈; 接受金鑰提供的每一種 COSE 演算法 | 主機的信任錨點是它在本機登記時記錄下的憑證金鑰, 所以證明鏈只會增加解析面而不增加信任; 單一演算法讓出貨的密碼學侷限在相依圖中已有的純 Rust p256 驗證器 (同一個 crate 也會編譯簽署的那一半, 但 WebAuthn 路徑永遠觸及不到它, 因為主機不持有憑證私鑰); 停滯的計數器意味著認證器已被複製或這是重放 |
策略簽章
| 規則 | 被拒絕的設計 | 原因 |
|---|---|---|
| 主機對儲存的策略位元組原樣簽章, 擴充功能對收到的位元組原樣驗證 | 簽章前先將 JSON 正規化 | 正規化是解析器差異的製造工廠; 沒有東西需要正規化, 正規化器裡就沒有東西可供利用 |
| 只收緊的策略不需要簽章, 以未簽章方式傳送; 只有授予才需要付出一次簽章 | 每次策略寫入都要求簽章 | 為例行的收緊彈出在場提示, 會教使用者把這種提示當成例行公事, 這正是點擊釣魚所需要的反射動作; 偽造的限制只能移除能力 |
MCP 伺服器行為
| 規則 | 被拒絕的設計 | 原因 |
|---|---|---|
已知缺口, 保留: 參數格式錯誤的 tools/call (沒有 name, 或 arguments 既不是物件也不是 null) 會在到達我們的處理器之前就被 rmcp 以 -32601 拒絕, 所以不會觸發任何 kill 檢查或稽核記錄 | 自己預先解析每個訊框以便稽核 | 那條路徑上不可能發生任何瀏覽器操作, 而在 SDK 旁邊再放一個解析器只會增加稽核面; 稽核日誌只記錄格式正確的呼叫 |
Native Messaging 協定
| 規則 | 被拒絕的設計 | 原因 |
|---|---|---|
run.lock 採寬鬆解析; 舊讀取者不得存活的變更改用新的、帶版本的檔名 | 像其他每一份磁碟上的記錄一樣, 拒絕鎖定檔中的未知欄位 | 鎖定檔是唯一一份舊版建置會在新版中介 (broker) 寫入時讀取的檔案, 而且它是用於探索, 不是授權; 換成新檔名後, 舊執行檔看不到鎖定檔, 就會失敗即關閉 |