1  序章:攻擊不再從正門進來

2021 年 12 月某個週五,全世界的 Java 工程師大概都記得。一個叫 Log4Shell(CVE-2021-44228) 的漏洞被公開:log4j——那個幾乎每個 Java 專案都在用、用到沒人記得自己有用的日誌函式庫—— 只要記錄一行攻擊者控制的字串,就能被觸發 JNDI lookup、遠端載入並執行任意程式碼。

那個週末,典型銀行環境裡的場景大同小異:所有人翻遍每一個系統,想回答一個聽起來很簡單的問題—— 「我們到底哪裡用了 log4j?」

然後發現答不出來。不是因為懶,而是因為沒有任何一張清單記著「每個系統各自帶進了哪些依賴的 哪些版本」。log4j 常常不是你直接引入的,它是你依賴的依賴的依賴(transitive dependency, 遞移依賴)。你的 pom.xml 乾乾淨淨,你的 WAR 檔裡卻躺著三個不同版本的 log4j-core

這就是這本書的起點:現代攻擊不從你寫的程式碼進來,從你「帶進來」的程式碼進來。

1.1 三個案例,三種進法

先預測一下:如果你是攻擊者,想讓惡意程式碼跑進一家銀行的機房,你會選哪條路? 直接打防火牆?釣魚拿 VPN?記住你的答案,看完下面三個真實案例再回來對。

1.1.1 Log4Shell:不是有人下毒,是所有人都在喝同一口井

嚴格說,Log4Shell 不是「供應鏈攻擊」——沒有人惡意植入後門,它是一個真誠的功能設計成了漏洞。 但它揭露了供應鏈問題的第一層:你根本不知道自己依賴什麼。一個漏洞出現在一個共用元件上, 爆炸半徑(blast radius)就是「所有用到它的系統」,而你連清單都沒有。

1.1.2 xz-utils:三年臥底,差一點就進了每一台 Linux

2024 年 3 月,xz-utils(Linux 幾乎必裝的壓縮工具庫)被發現藏了後門(CVE-2024-3094)。 這次是真的有人下毒,而且手法讓所有人背脊發涼:一個化名 “Jia Tan” 的帳號花了大約三年 在這個開源專案做出貢獻、取得信任、成為維護者,然後把一個精心偽裝的後門藏進發行的 tarball ——後門甚至不在 git 原始碼裡,只在「打包出來的發行版」裡,目標是讓 liblzma 掛進 sshd、 在 SSH 認證階段開洞。它已經進了多個發行版的測試版,離全面鋪開只差幾週, 被一位工程師抓到的原因荒謬得像小說:他注意到 SSH 登入慢了約 500 毫秒,追下去了。

這揭露第二層:「開源、大家都在用、維護者活躍」不等於可信。信任放在「人」身上, 而人是可以被長期經營的。

1.1.3 npm 惡意套件:直接對水源下手

npm 生態的攻擊已經是日常:typosquatting(搶注 lodash 打錯字的 loadsh)、 dependency confusion(用同名公開套件劫持公司內部套件)、或像 2018 年 event-stream 事件—— 原維護者無力維護,把套件交給熱心網友,熱心網友加上針對特定加密貨幣錢包的竊取程式碼。 每週有數百萬次下載的套件,換一個維護者,就換了一個信任等級,而下游沒有任何人會收到通知。

這揭露第三層:你每天 npm installmvn package 的動作,本質是「把陌生人剛上傳的程式碼 直接拉進你的建置」——而這條路上幾乎沒有驗證。

回到剛才的預測:攻擊者為什麼要打你的防火牆?從依賴進來,你會親手把他 build 進系統, 再簽核上線。正門有警衛,後門是你自己開的。

注记隱性授予:這三個案例其實都是同一件事

你可能會想:Log4Shell 沒有人下毒,怎麼算「信任」問題?但授予其實發生了,只是你沒意識到。 把 log4j 拉進專案的那一刻,你就授予了它「可以在我的行程裡執行任意行為」的權限—— 而且多數時候是遞移授予:你信任的直接依賴,代替你把這份信任發了出去。

這就是為什麼密碼學在 Log4Shell 上幫不上忙:簽章是真的、套件真的是那個專案發布的。 問題從來不在「是不是那份」,而在「該不該給它那麼大的權限」。全書主線因此更精確一點: 信任是被授予的——而最危險的那種授予,是你不知道自己給出去的。

1.2 六個攻擊面:從原始碼到生產的每一步都能下手

把「一支軟體怎麼從工程師的手到生產環境」攤開,每個環節都是攻擊面:

flowchart LR
  S["① Source<br/>原始碼"] --> B["② Build<br/>建置"]
  B --> A["③ Artifact<br/>成品"]
  F["④ Feed / Registry<br/>套件源"] --> B
  A --> D["⑤ Deploy<br/>部署"]
  D --> P["⑥ Production<br/>生產"]
  S -.提交惡意 code、盜帳號.-> S
  B -.污染 build 環境(SolarWinds).-> B
  A -.掉包成品、tarball 藏後門(xz).-> A
  F -.惡意套件、typosquat、confusion.-> F
  D -.部署時替換.-> D
  P -.執行期拉惡意更新.-> P
图 1.1: 軟體供應鏈的六個攻擊面。每個箭頭都是一次「信任移交」——也都是一個下手點。
攻擊面 攻擊手法舉例 對應的信任問題
① Source(原始碼) 盜開發者帳號提交、惡意 PR、長期臥底(xz 的社交面) 這段 code 真的是這個人寫的嗎?
② Build(建置) 污染 build server——SolarWinds 事件即在此下手,成品被植入後門再由官方簽章發佈 build 出來的東西=source 的忠實翻譯嗎?
③ Artifact(成品) 發行 tarball 與 git 內容不一致(xz 的技術面)、artifact 庫被掉包 我拿到的檔案=當初 build 出的那份嗎?
④ Feed / Registry(套件源) typosquatting、dependency confusion、維護者易手(event-stream) 這個套件名背後現在是誰?
⑤ Deploy(部署) 部署管線替換 image、tag 漂移(同一個 tag 指到不同內容) 上線的=通過測試簽核的那份嗎?
⑥ Production(生產) 執行期自動更新拉到惡意版本 跑著的東西還是我驗過的東西嗎?

注意一件事:這六個問題全部可以歸結成同一句話——

「我手上這份東西,跟某個我信任的人當初做出來的那份,是不是同一份?」

這句話就是整本書要回答的問題。而回答它的工具,不是防火牆、不是 WAF, 是三個古老的密碼學原語:hash、數位簽章、MAC

1.3 本書路線圖

第一部不急著談供應鏈,先把地基打穿:

  • 第 2 章:數位簽章——「私鑰簽、公鑰驗」到底在保證什麼;為什麼簽的是 hash 不是原文。 附一支你可以親手跑的實驗:簽一個檔案、改一個 byte、看驗章當場失敗。
  • 第 3 章:PKI 與信任鏈——簽章要靠公鑰驗,但公鑰憑什麼可信?憑證、憑證鏈、 以及那個常被說破嘴的結論:鏈的終點不是數學,是人為決定。
  • 第 4 章:認證加密(深入,可略讀)——簽章+加密一起用時,兩種直覺順序都有洞; MAC、Encrypt-then-MAC、AEAD。
  • 第 5 章:三原語的能力邊界——把 hash / 簽章 / MAC 各自「給什麼、不給什麼」畫成一張表, 並把六個攻擊面逐一對回「哪個原語能堵、哪個堵不了」。這張表是第二部的地圖。

第二部(撰寫中)把原語套到真實供應鏈:NIST SSDF(Secure Software Development Framework, 安全軟體開發框架)的要求、成品簽章與 provenance、以及在典型銀行環境的 CI/CD 裡實際落地的紀錄。

1.4 誠實邊界

重要開始前最後一次對齊

本章的案例敘述全部來自公開報導與公開技術分析,細節以原始揭露為準。書中出現的環境描述 一律是「典型銀行環境」的去識別重構,不對應任何真實機構。這本書是我的學習與實作紀錄—— 它能給你的是心智模型與可重跑的實驗,不是合規背書。


本章帶走的東西:現代攻擊面是「你帶進來的程式碼」,從 source 到 production 有六個下手點; 它們共用同一個核心問題——「我手上這份,是不是我信任的人做的那份」。下一章從能回答這個問題的 第一塊積木開始:數位簽章。