資安與權限設計

前門鎖好了,駭客改走側門:npm 安裝腳本防線兩個月就被繞過

npm 在 7 月推出的 v12 預設停用相依套件的安裝腳本,但 ReversingLabs 10 月 6 日的報導指出,惡意套件 indexed-btree 把觸發點藏在函式庫方法裡,使用時才發作,輕鬆繞過這道防線。同週 @subql/common 也因 CI/CD 被竄改而發布惡意版本。我們整理事件始末,以及網站專案該如何分層防禦。

前門鎖好了,駭客改走側門:npm 安裝腳本防線兩個月就被繞過
前門鎖好了,駭客改走側門:npm 安裝腳本防線兩個月就被繞過

npm 12 預設不再執行安裝腳本,但惡意套件改成「被呼叫時才發作」。你的網站依賴套件,該換個方式把關了。

想像你家大樓終於裝了門禁系統:訪客進門要刷卡、要登記。管委會很滿意,住戶很安心。然後某天你發現,小偷根本不走大門——他直接混進搬家公司的紙箱裡,等你自己把箱子搬上樓、打開。

這差不多就是 npm 生態系這幾週發生的事。根據 ReversingLabs 部落格 10 月 6 日的報導,npm 在 7 月推出的一項重要安全措施,不到兩個月就已經被攻擊者繞過了。

如果你的網站前端用 React、Vue、Next.js,或任何需要 npm install 的工具,這篇跟你有關。

先補背景:npm 12 做了什麼?

長期以來,npm 套件可以在 package.json 裡定義 preinstall、postinstall 這類「生命週期腳本」,在安裝時自動執行。這很方便,但也一直是供應鏈攻擊的熱門入口:你只是裝個套件,惡意程式就跑起來了。

ReversingLabs 報導指出,npm 在 7 月推出的 version 12,預設不再執行相依套件的安裝腳本,目的就是關掉這條長年存在的攻擊路徑。

聽起來很棒,對吧?問題是,攻擊者也看得懂更新日誌。

事件一:indexed-btree——不在安裝時發作,在使用時發作

研究人員發現一個持續進行中的供應鏈攻擊活動,主角是惡意套件 indexed-btree,它模仿合法的 sorted-btree 函式庫。Checkmarx 的 Bruno Dias 在部落格中說明:這個套件沒有使用 preinstall 或 postinstall 腳本,而是把惡意觸發點藏在套件自己的 prototype 方法裡,在函式庫被使用的那一刻才觸發。

一旦執行,這個惡意程式會蒐集主機特徵、透過 Slack 和 Telegram 外傳資料,並利用以太坊智慧合約作為具韌性的指揮控制(C2)通道。

Checkmarx 的研究倡議者 Darren Meyer 一句話總結:「攻擊者只是換了一條路。」("Attackers just moved to a new vector.")

「乾淨的 package.json」反而成了盲點

Secure.com 工程主管 Waseem Ahmed 的觀察很犀利:沒有安裝腳本,已經悄悄變成一種「信任訊號」——審查的人看一眼 package.json,沒看到 hook,就放心了。而這個套件的 package.json 完全乾淨,惡意程式就躲在函式庫方法裡。他說:「如果你在安裝時把門關上,有耐心的攻擊者就往下游走一步,改在 import 時執行。」

更麻煩的是偽裝。KhaiCode 執行長 Aviram Jenik 指出,攻擊者還準備了看起來很可信的假 GitHub 儲存庫、提交紀錄,以及附照片的開發者個人檔案;公開儲存庫裡完全沒有惡意程式碼,所以掃描它什麼也找不到。他形容這種「使用時才啟動」的惡意程式就像一個等待啟動的「潛伏小組」(sleeper cell)。

事件二:@subql/common——連 CI/CD 都被動了手腳

同一週,GMO Flatt Security 在 10 月 6 日發布另一起事件分析:10 月 5 日,npm 套件 @subql/common 被注入惡意程式碼。

這次的手法不一樣:攻擊者取得了 GitHub 儲存庫的推送權限,竄改 CI/CD 工作流程,再透過 GitHub Actions 的 OIDC(Trusted Publisher)發布惡意版本 5.8.3,讓它看起來跟正常發布幾乎無法分辨。惡意版本會透過 postinstall 執行,也會在被 require() 時啟動,目標包括 AWS、GitHub、Kubernetes、Vault 的憑證,以及 .env、SSH 金鑰等敏感檔案——清單裡甚至有 wp-config.php。

Flatt Security 也觀察到一個細節:這個惡意提交沒有 GPG 簽章,而該開發者過去的合法提交都有。

文章還列出今年一連串 npm 供應鏈事件:3 月的 axios、4 至 5 月的 Mini Shai-Hulud、7 月的 AsyncAPI、8 月的 keyv,再到這次的 @subql/common。

為什麼這對一般企業網站也重要?

你可能會想:「我們只是一個形象官網,又不是科技公司。」但現代網站的前端建置、CMS 外掛、後台工具,幾乎都依賴大量開源套件。Solid Software Solutions 的 Sonu Kapoor 提醒:惡意相依套件不一定在安裝時作怪,它可以等到應用程式使用它時才行動,那時它可能正跑在可以存取憑證、內部系統與客戶資料的正式環境中。

Suzu Labs 的 Jacob Krell 說得更直白:封鎖生命週期腳本仍然有用,但它是「減速丘」,不是套件信任模型。

兩起事件的共同點:攻擊者專挑「大家放心的地方」

把兩起事件放在一起看,會發現一個很一致的模式:

  • indexed-btree 挑的是「沒有安裝腳本=安全」這個新習慣。
  • @subql/common 挑的是「透過官方 CI 與 Trusted Publisher 發布=可信」這個印象。Flatt Security 甚至發現,同一流程發布的測試版本帶有 SLSA 來源證明(provenance),因此當攻擊者已經控制發布管線時,光看有沒有 provenance 也分辨不出惡意版本。

換句話說,每一個被大家當作「安心訊號」的檢查點,都可能成為下一個被利用的盲點。這不是要大家什麼都不信,而是提醒我們:不要把信任押在單一指標上。

不是工程師也能問的 5 個問題

如果你是網站或系統的擁有者,不必懂每個技術細節,但可以向開發團隊(或委外廠商)問這幾題:

  1. 我們的專案用了哪些第三方套件?有沒有定期檢視與更新的流程?
  2. 新增或升級套件時,誰審查?有沒有冷卻期?
  3. 建置與部署環境裡放了哪些金鑰?權限是不是最小必要?
  4. 誰可以修改 CI/CD 設定?有沒有紀錄與審核?
  5. 如果某個套件被爆出有問題,我們多快能知道、多快能輪替金鑰?

答得出來的團隊,通常也比較讓人放心。

實戰清單:網站專案的套件安全分層防禦

  1. 維持停用安裝腳本:npm 12 已預設停用;如果 CI/CD 混用不同 npm 版本,Flatt Security 建議仍明確使用 npm ci --ignore-scripts。
  2. 設定套件冷卻期:Flatt Security 建議在 .npmrc 設定 min-release-age(npm v11 以上支援),建議 7 天,趕時間也至少 3 天,避免第一時間裝到剛發布的惡意版本。
  3. 鎖定版本、審查更新:使用 lockfile,相依套件更新走 Pull Request 審查,而不是隨手升級。
  4. 新套件先做身家調查:名稱是否在模仿知名套件?維護者、下載量、儲存庫是否合理?但也記得,假儲存庫可能做得很逼真。
  5. 最小權限原則:建置與部署環境只放必要的憑證,CI 的 token 權限盡量縮小,正式環境的金鑰不要出現在開發機。
  6. 保護 CI/CD 本身:啟用分支保護、要求提交簽章、限制誰能修改 workflow 檔案。
  7. 監控執行期行為:多位專家都強調,只看安裝階段不夠,要留意應用程式執行時的異常網路連線與行為。
  8. 準備事件應變流程:知道去哪裡查 lockfile、怎麼盤點受影響環境、怎麼快速輪替金鑰。

你可能會問

Q:我們升級到 npm 12 了,是不是就安全了? 比較安全,但不是絕對安全。正如 Darren Meyer 所說,停用生命週期腳本有安全價值,但「它無法阻止感染,只是讓攻擊換了路徑」。

Q:如果發現專案裝過可疑套件,刪掉就好嗎? 不夠。Black Duck 的 Boris Cipot 提醒,應該確認套件出現在哪些環境、是否曾執行、調查系統是否有可疑活動與憑證外洩,必要時從可信來源重建環境。Flatt Security 也建議輪替所有可能外洩的金鑰與 token。

Q:我們不是寫 JavaScript 的公司,跟我們有關嗎? 只要網站有前端建置流程、用了 Node.js 工具,或委外團隊用 npm 套件開發,就有關。這也是挑選合作團隊時值得問的問題:你們怎麼管理相依套件?

結語:信任要靠多層,而不是一道門

這次事件最大的教訓不是「npm 不安全」,而是任何單一防線都會被繞過。安裝腳本停用了,攻擊就移到執行期;發布流程有簽章與 Trusted Publisher,攻擊者就去動 CI/CD 本身。

好的資安設計從來不是一道堅不可摧的門,而是一層層的檢查、最小權限,加上出事時能快速應變的流程。

如果你想檢視網站專案的相依套件管理、CI/CD 權限設計,或建立一套金鑰輪替與事件應變流程,歡迎找我們聊聊。我們可以一起把門鎖好,也把側門、後門和紙箱都顧好。


資料來源:Dependency installation security measure already defeated on npm — ReversingLabs Blog(John P. Mello Jr.,2026/10/06)

延伸參考:Software Supply Chain Attack on @subql/common: Overview and Response Guidance — GMO Flatt Security Research(2026/10/06)

← 返回技術文章