論述基礎與限制

這張卡來自一組文章的撰寫與五輪審查:單台主機的監控盲區、第三方心跳監看、兩到三台主機的配置,以及兩個網路之間的連線安全。同一種缺陷在四篇的初稿裡反覆出現,每一次都被當成已經解決,而每一次被指出時的形狀都不同:

  • 心跳監看放在另一個業者上,被判定為獨立於監控主機;而心跳只證明它自己那一條路通,通知管道的網址寫錯時心跳照常送達,人收不到任何告警。
  • 同一個心跳監看,在監控主機被入侵時看不出異狀:攻擊者手上有監控主機存著的心跳網址,停掉告警鏈之後自己送心跳。
  • 外部探測被判定為不經過監控主機的觀察者,而它的告警寄到監控主機也存著密碼的信箱,攻擊者登入信箱設一條過濾規則就刪得掉。
  • 防火牆限制寫在「另一端」,被判定為被入侵的雲端主機改不到;而雲端主機上存著能改 ACL 的 API 金鑰、能改業者防火牆的 token、能推送部署設定的金鑰。
  • 備份放在另一個業者,被判定為與服務主機分開;而寫入備份的存取金鑰就在服務主機上,上傳權限本身包含覆寫。
  • 評估時「兩端各假設一次被入侵」,漏掉管理者的筆電:它存著兩台主機的 SSH 金鑰,被入侵時兩端的每一條限制都失效。

證據的限制:六個實例出自同一個作者、同一組文章、同一段時間,審查者與作者共用同一個模型;跨主機的限制效果沒有實機驗證。卡上的原則是從這組實例歸納的,換到別的領域(例如稽核紀錄的獨立性、CI 的簽章驗證)是否同樣成立,尚未對照。

核心原則

一個觀察者或一條限制獨立於被保護的對象,條件是被保護的一方沒有任何方式讓它失效。這要同時檢查三件事:

共用的失效域:它與被保護的對象有沒有共用會一起停掉的依賴(同一台主機、同一個業者帳號、同一條網路、同一個 DNS)。共用時,被保護的對象停掉,它跟著停。

寫入路徑:被保護的一方(或被假設入侵的那一台)能不能改寫它,方式包括存在那台機器上或它取得得到的存取憑證(API 金鑰、業者 token、雲端實例角色)、已登入的工作階段(管理介面、信箱、業者主控台)、它能推送的設定來源、它在控制平面上的角色(簽署節點、自架的協調伺服器)。有任何一條,存放在別處的限制仍然改得到。

輸出管道:觀察者的結論要送到人手上,送的那一段(通知管道、收件信箱、推播頻道)也要通過前兩項的檢查。觀察者本身獨立而通知經過被保護的一方,結果與沒有觀察者相同。

三件事都通過才算獨立。評估的單位是每一台會被假設入侵的機器,包括由人操作的裝置;管理者的筆電通常持有最寬的寫入路徑,因為它存著登入每一台主機的憑證。

反模式:以存放位置判斷獨立

最常見的判斷是「它放在另一台/另一個業者,所以獨立」。存放位置是三件事的第一步,而它回答的只有三件事裡的失效域:被保護的一方手上有沒有改寫它的憑證、它的輸出經過哪裡,它都沒有回答。

這個判斷在故障情境下多半成立,在入侵情境下多半不成立。故障時被保護的對象是停掉了,停掉的機器不會去改寫別台;入侵時被保護的對象還在運作,而且由一個會去找寫入路徑的人操作。同一個觀察者,故障演練全數通過,入侵情境下一步就被繞過。所以一份只做過故障演練的設計,對入侵的獨立性沒有任何證據。

另一個形態是固定角色:先決定「雲端是會被入侵的那一端、地端是可信的那一端」,之後所有的判斷都以這個指派為前提。它讓地端被入侵、管理者的裝置被入侵這兩個方向完全不被檢查,而兩者在一般的配置裡都不罕見(地端主機兼作日常電腦、管理者的筆電用來收信)。

為什麼前幾輪審查抓不到

這個缺陷在每一輪都換了形狀,而每一輪的修法都只補了被指出的那一個實例:心跳的通知管道被補上了獨立的檢查項目,限制的「另一端」被補上了「前提是沒有寫入路徑」,備份被補上了物件鎖定。每一次修法都讓那一處正確,同時讓整份設計看起來更完整,而缺的那一個維度(被保護的一方能不能讓它失效)沒有被寫成判斷標準本身,所以下一個新加入的成員(管理者的筆電、簽署裝置、清除舊備份用的機器、對外的儀表板主機)又照著存放位置被判定為獨立。

偵測它的是個案實跑:拿四個極端個案(個人裝置混在同一個 VPN、兩台都對外、推送方向與 NAT、地端被入侵)照判斷標準走一次,個案停住的地方並排之後才看出缺的是同一個維度。逐段閱讀的審查對它的偵測率很低,因為每一段單獨讀都說得通。

修法

逐台列出會被假設入侵的機器,含由人操作的裝置。每一台、對每一個要依賴的觀察者與限制,問三題並寫下答案:

  1. 它與這一台共用哪些會一起停掉的依賴。
  2. 這一台有沒有任何改寫它的方式:存著或取得得到的憑證、已登入的工作階段、能推送的設定來源、控制平面上的角色。逐項列,不憑印象。
  3. 它的輸出(告警、通知、紀錄)送到人手上的那一段,是否也通過前兩題。

任何一題的答案是「有」,那個觀察者或那條限制對這一台不獨立:把它搬到這一台改不到的地方、拿掉這一台的那條寫入路徑,或在設計文件裡寫明這一台被入侵時它不成立。

由人操作的裝置要單獨處理:它的寫入路徑通常無法在網路層收窄,因為它走的是本來就允許的管理連線。能切斷的做法是讓管理只從一台不日常上網的專用裝置進行;硬體金鑰、再驗證這類做法只縮小範圍。

判讀徵兆

  • 設計文件寫「放在另一台」「放在另一個業者」,而沒有列出被保護的那一台上有哪些憑證。
  • 只做過故障演練(停掉主機、拔網路),沒有做過「假設這一台被拿下」的推演。
  • 修法一再補上「前提是那一台沒有能改寫它的憑證」這類句子,而判斷標準的定義本身仍然只看位置。
  • 個案推演得出「這一端的限制都改不到,所以安全」,而攻擊者走的是清單上本來就允許的連線。
  • 觀察者的告警寄到的信箱或推播頻道,其帳號存在被觀察的機器上,或存在管理者每天用的那台電腦上。
  • 備份寫入憑證放在服務主機上,而設計文件說的是「備份在另一個業者,所以安全」。

跟其他抽象層原則的關係

  • #257 軸名取了次好的代理變數:「存放位置」是「寫入路徑」的代理變數,正確的機制最初只出現在修法的括號裡(「前提是沒有能改寫它的憑證」),這張卡把它提升為判斷標準本身。
  • #297 判斷標準只收表徵:固定角色是同一種折疊,「哪一台被假設入侵」這個輸入在指派之後就從判斷標準裡消失,個案實跑時才浮現。
  • #283 替補結構的豁免來自它修掉的清單:每一輪補上的修法(獨立的通知檢查、「每端各假設一次」)都修掉了被指出的實例,而它們引進的新成員沒有被同一個問題檢驗;這張卡的修法要求對新加入的成員重跑三題。
  • #337 審查的射程只涵蓋被審查的對象:同構的另一面。審查者不會審查自己用的工具,觀察者的獨立性也常常沒有把「觀察者自己依賴什麼」算進去。
  • #296 規模買不到異源視角:在審查流程上的同一個原則。同一個 session 派出的審查者與作者共用盲區,數量增加不構成獨立;判斷的是兩者共用了什麼,不是有幾個。