繁體中文English
jason's blog關於網頁標準、瀏覽器與前端開發的觀察筆記

文章分類: 資訊安全

ChainDrop:當 npm 惡意套件開始自己擴散

從 ChainDrop 事件理解 npm 惡意套件如何竊取憑證、自我擴散,以及前端團隊能做的防護。

發布 2026/08/04

老實說,我看到「又有 npm 供應鏈攻擊」時,第一個反應其實有點麻木。這兩年同類新聞太多了,每次都是提醒大家鎖版本、換 token,過幾天又回去照常 npm install。但 8 月這次 ChainDrop 我看到一半真的停了下來:它不是只污染一兩個冷門套件,而是會偷走發布憑證,再把自己塞進受害者有權發布的其他套件裡。

事情從 keyv、cacheable 這些常見套件家族擴散出去。惡意版本利用 preinstall 在安裝階段執行,下載第二階段程式、搜尋雲端與 CI 憑證,接著拿偷到的 npm token 繼續發布被污染的版本。研究單位在短時間內觀察到數百個套件、上千個惡意版本,相關套件合計每月下載量更是以十億計。這個規模很難再用「不要裝奇怪套件」帶過。

我覺得最麻煩、也最讓人無力的地方,是開發者不需要做出明顯錯誤。套件名稱是熟的、來源是真的,甚至發布流程看起來也可能有 provenance。你只是正常更新 dependency,安裝腳本就已經在測試開始前執行了。

它為什麼能擴散得這麼快?

  • 先從維護者帳號或發布流程取得入口,再發布帶有惡意 preinstall 的版本。

  • 安裝時竊取 npm、GitHub、雲端與 CI 憑證,尋找受害者還能發布哪些套件。

  • 用被竊取的權限發布下一批惡意版本,讓一次入侵沿著維護權限繼續往外長。

這也解釋了為什麼 lockfile 不是護身符。它能保證今天和 CI 安裝到相同版本,卻不能判斷那個版本是不是惡意的。如果更新 PR 已經把污染版本正常寫進 lockfile,npm ci 只會非常忠實地把它裝起來。想到這裡其實滿諷刺的:我們為了可重現性做對的事,仍然可能穩定重現一次攻擊。

如果是我在維護專案,我會先做什麼

我不會先期待 npm audit 給答案,因為這類剛發生的惡意版本未必已經進入漏洞資料庫。第一步會先比對事件時間、lockfile 和 CI 安裝紀錄,確認哪些機器真的碰過受影響版本;只要安裝過,就把當時環境裡可讀到的憑證視為可能外洩,而不是只刪掉 node_modules 就算了。

接著才是輪替 token、撤銷 session、檢查異常發布與雲端操作。這部分很煩,而且很可能會打斷原本的工作,但憑證竊取和一般套件漏洞不同:把 dependency 降版只能停止惡意程式繼續跑,已經被帶走的鑰匙不會自己回來。

長期來看,我會把 install 和 publish job 分開,不讓安裝第三方 dependency 的工作同時拿著發布權限;能用 Trusted Publishing 和短效 OIDC token,就不要在 CI 放一把半年不換的 npm token。再加上 frozen lockfile、版本冷卻時間和限制 install script,沒有任何一項能單獨擋住 ChainDrop,但至少可以讓它少跨過幾道門。

最可惜的是,JavaScript 生態的便利和這種風險其實來自同一件事:我們很擅長把別人的小工具組進專案。要大家從此不信任 npm 不切實際,我自己也做不到。比較誠實的做法,是承認每次安裝都在執行一段供應鏈,然後讓那段流程就算出事,也拿不到整間公司的鑰匙。