npm 套件接連遭入侵後,如何安全執行 `npm install`?
NPM Is Totally Compromised. How Do You Run `npm install` Safely Now?
作者整理 npm、pnpm、Yarn 和 Bun 的依賴安裝防護做法,説明如何降低惡意套件版本進入開發環境和 CI 的風險。建議設定 7 天新版本冷卻期、限制安裝腳本、提交鎖定檔並在 CI 使用凍結安裝;文中列出 npm 11.10.0+、pnpm 11+、Yarn 4.12+ 和 Bun 1.3+ 的相關設定。
文章比較 npm、pnpm、Yarn 和 Bun 的版本冷卻、安裝腳本與鎖定檔設定,並説明這些措施無法取代 CI 權限控管及惡意程式碼檢查。
你很少會想到,在日常執行 npm install 時,有甚麼程式碼會安裝到你的裝置上,或安裝後可能會做甚麼。你預期遇到的是建置錯誤或版本衝突。遭入侵的套件卻可能竊取你的 SSH 金鑰、GitHub 權杖、AWS 憑證,以及加密貨幣錢包的存取權,然後利用你的憑證感染你自己的套件。
你用了多年的程式庫,也可能出現惡意發佈版本,並捲入透過其他套件蔓延的一連串入侵事件。
單是 2026 年 9 月和 10 月,就有不少例子:
-
SubQuery。研究人員在
@subql/common@5.8.3中發現了竊取憑證和遠端命令執行程式碼。其他 SubQuery 相依套件可令該套件進入專案。StepSecurity 的分析。 -
Tensorlake。版本
tensorlake@0.5.144會竊取權杖、嘗試擴散至其他套件,並安裝一個背景程序;如果遭竊的 GitHub 權杖被撤銷,該程序便會刪除用戶的家目錄。StepSecurity 的分析。 - MemTensor。AI 外掛程式的惡意發佈版本會蒐集憑證。進行記憶檢索時,該外掛程式也會把用戶的提示詞傳送給一個隱藏程序。StepSecurity 的分析。
與此同時,Shai-Hulud 家族的攻擊仍在持續。這種攻擊方式對套件維護者尤其棘手:蠕蟲會竊取發佈權杖,把惡意程式碼注入受害者有權發佈的套件,再以受害者名義發佈新版本。Unit 42 解釋了這種手法及其後的攻擊行動。
2026 年較早時,連 Axios 也遭到入侵。惡意發佈版本引入了額外的相依套件,並安裝遠端存取木馬。Socket 的分析。
TanStack 套件也遭到攻擊。遭入侵的發佈版本包含針對 CI 憑證的程式碼,以及一項 Git 相依套件;該套件會透過其 prepare hook 執行惡意指令碼。Socket 的另一份分析。
你的 package.json 可能完全不會變更:
{
"dependencies": {
"some-package": "^1.2.3"
}
}
這個版本範圍允許 semver 視為相容的較新版本,例如 1.3.4。如果套件管理程式下次解析相依套件時選用了遭入侵的發佈版本,日常安裝便會成為攻擊入口。
npm 和其他套件管理程式需要重新思考我們處理相依套件的方式。與此同時,你可以進行幾項設定,降低裝置和 CI 環境中的風險。
1. 為新發佈版本設定冷卻期
我會先啟用的第一項設定,是延遲安裝新發佈的版本。以下例子會使用一星期:如果某個版本在七日內發佈,暫時不要安裝。就我個人而言,我認為三日也足夠。
這樣研究人員和維護者便有時間發現遭入侵的發佈版本、提醒用戶,並將其從套件登錄庫移除。
適用於 npm 11.10.0+:
# .npmrc
min-release-age=7
此處的值以日為單位。此選項由 npm 11.10.0 引入。如果沒有任何可用版本同時符合版本時長要求和相依套件條件,安裝便會失敗。設定參考。
適用於 pnpm 11+:
# pnpm-workspace.yaml
minimumReleaseAge: 10080
minimumReleaseAgeStrict: true
此處的值以分鐘為單位:10080 即七日。設定 minimumReleaseAgeStrict: true 後,即使沒有較舊版本符合相依套件條件,pnpm 也不會選用較新的發佈版本。
pnpm 11 預設會延遲一日,但如果沒有啟用嚴格模式,仍可能改用發佈時間太近的版本。請明確設定此政策。pnpm 文件。
適用於 Yarn 4.12+:
# .yarnrc.yml
npmMinimalAgeGate: "1w"
Yarn 預設亦會啟用一天的延遲。這會將延遲時間增至一星期。Yarn 説明文件。
適用於 Bun 1.3+:
# bunfig.toml
[install]
minimumReleaseAge = 604800
Bun 使用秒作單位:604800代表七天。此設定於 Bun 1.3 引入。
不過要注意:Bun 只會在解析新版本時套用篩選器。已記錄在 bun.lock 中的相依套件會維持不變,因此加入此設定不會追溯檢查整個鎖定檔。篩選器的運作方式。
等候一星期並不能令套件變得安全,但可以降低在惡意版本發佈後、有人發出警報前立即安裝該版本的機會。
緊急安全修正可能需要更早推出。檢視某個特定更新後,可為該更新設立例外。
2. 限制自動安裝指令碼
套件可以透過 preinstall、install 和 postinstall 執行指令。有些相依套件有合理原因需要這樣做,例如建置原生模組或下載二進制檔。
這些指令會以啟動安裝程序的用戶權限執行。如果該用戶可以存取 SSH 金鑰、.npmrc 中的權杖,或環境變數中的機密資料,指令碼亦可能取得這些資料。
適用於 npm,你可以完全停用自動指令碼:
# .npmrc
ignore-scripts=true
這會在安裝期間停用自動執行指令碼。npm run build 等明確執行的指令仍然可以使用。npm 説明文件。
從 npm 12 開始,除非 allowScripts 政策允許,否則預設會封鎖相依套件的安裝指令碼。因此,「npm 一直會執行每個套件的所有指令碼」這種説法已不適用於較新版本。npm 12 版本資訊。
使用 pnpm 11+ 時,第三方相依套件的指令碼亦需要獲得批准。你可以在 pnpm-workspace.yaml 設定例外:
# pnpm-workspace.yaml
allowBuilds:
esbuild: true
core-js: false
這是語法示例;只有在檢視套件的指令碼後,才批准該套件。未有列出的相依套件會視為未經審核。在預設設定下,如果這些套件需要建置,pnpm 會令安裝失敗。
如要停用專案及其相依套件的所有安裝指令碼:
# pnpm-workspace.yaml
ignoreScripts: true
啟用此設定後,allowBuilds 項目也不能推翻這項禁令。請選擇停用所有指令碼,或只允許特定相依套件。pnpm 説明文件。
適用於 Yarn 4:
# .yarnrc.yml
enableScripts: false
Yarn 4.14 起預設採用此設定。此設定會限制第三方套件的指令碼,但你自己工作區中的指令碼仍會執行。你可以透過 dependenciesMeta 允許個別相依套件。Yarn 説明文件。
使用 Bun 時,請將設定儲存在專案設定檔中:
# bunfig.toml
[install]
ignoreScripts = true
如果你已加入 minimumReleaseAge,請將兩項設定放在同一個 [install] 區段下:
# bunfig.toml
[install]
minimumReleaseAge = 604800
ignoreScripts = true
即使沒有這項設定,Bun 也會限制相依套件的指令碼,但內置了一份受信任套件清單。因此,聲稱 Bun 預設不會執行安裝指令碼並不準確。
如果你需要選擇性批准,而非一律封鎖,請在 package.json 中使用 trustedDependencies。清空清單會取消對所有相依套件的信任,包括內置例外項目:
{
"trustedDependencies": []
}
你可以將已審核的套件加入清單。使用 ignoreScripts = true 時,無論你信任哪些套件,指令碼都會維持停用。Bun 説明文件。
更改這些設定後,部分相依套件可能無法安裝或正常運作。你需要查明它們為何需要執行指令碼,再決定是否允許。
限制安裝指令碼只能處理部分問題。惡意程式仍可在一般import、建置或應用程式執行期間運作。在 MemTensor 事件中,執行行為本身已納入外掛程式的正常運作。
3. 提交鎖定檔,並在 CI 中使用
將鎖定檔與程式碼一同存放在儲存庫中。鎖定檔會記錄直接及傳遞依賴項的指定版本。
在 CI 中使用套件管理程式的相應指令:
| 套件管理程式 | 鎖定檔 | 指令 |
|---|---|---|
| npm | package-lock.json |
npm ci |
| pnpm | pnpm-lock.yaml |
pnpm install --frozen-lockfile |
| Yarn 4 | yarn.lock |
yarn install --immutable |
| Bun | bun.lock |
bun ci |
這些指令會根據鎖定檔安裝套件;若鎖定檔需要變更才能符合package.json,指令便會失敗。請參閲npm、pnpm、Yarn及Bun的説明文件。
對 Bun 而言,bun ci等同於bun install --frozen-lockfile。請確保提交bun.lock:一般bun install不會單因在 CI 中執行而自動啟用凍結模式。
你亦可從直接依賴項中移除^和~,但這不能取代鎖定檔。程式庫各有自己的依賴項。
鎖定檔也不會檢查惡意程式。如果其中已有遭入侵的版本,可重現的安裝仍會繼續安裝該版本。請在個別 PR 中處理依賴項更新,以便審查哪些套件及版本有所變更。
建置專案時,不應同時決定要更新依賴項。
你可以將預設設定為儲存精確版本,這樣新增依賴項時會將"1.2.3"寫入package.json,而非"^1.2.3"。
npm:在.npmrc中加入以下內容:
save-exact=true
新增的依賴項將不會附帶版本範圍。npm 説明文件。
pnpm 11+:在pnpm-workspace.yaml中加入以下內容:
saveExact: true
Yarn 4:在.yarnrc.yml中加入以下內容:
defaultSemverRangePrefix: ""
空字串會停用自動加入^或~的功能。Yarn 説明文件。
Bun:在bunfig.toml中,將exact加入現有的[install]部分:
[install]
exact = true
這些設定會改變新增依賴項的儲存方式,但不會自動改寫現有包含^或~的項目。
固定版本會為直接依賴項指定版本,但該套件本身的依賴項仍可能使用版本範圍。你仍需要使用鎖定檔,並在 CI 中進行凍結安裝。之後可以透過個別 PR 更新固定版本,並在合併前審查變更。
4. 檢查依賴項更新是否含有惡意程式
npm audit有助找出已知漏洞,但不要只依賴它。昨天才發布、今天便竊取權杖的版本,可能尚未有 CVE 或傳統漏洞資料庫的記錄。
Socket、Aikido Security及 StepSecurity 等工具可以檢查依賴項是否含有惡意程式或可疑行為。舉例來説,StepSecurity 分別設有檢查已知遭入侵版本及過新發布版本的功能,詳情見其Tensorlake 分析。
請在合併前,對依賴項更新 PR 執行這些檢查。這樣你便能在仍可阻止更新時收到警告。
5. 避免安裝依賴項的工作接觸機密資料
查看執行npm install的程序可使用哪些權限及機密資料。它需要正式環境憑證嗎?需要發布套件的權限嗎?需要一個可寫入所有儲存庫的權杖嗎?
該程序可使用的權限越少,惡意依賴項能竊取或更改的內容就越少。
將建置與部署分開,只向需要機密資料的工作提供這些資料,並將GITHUB_TOKEN的權限限制在完成當前任務所需的範圍內。
也要將第三方 GitHub Actions 固定至完整的 commit SHA。即使工作流程檔案完全不變,可移動的標籤仍可能指向不同的程式碼。
透過 CI 發動的攻擊已大規模發生。2026 年 10 月,另一波 GhostAction 攻擊影響了超過 30,000 個 GitHub 存放庫,並注入旨在竊取機密資料的工作流程。GitGuardian 的調查。
最終設定
請在專案中明確儲存這些設定,避免安裝行為取決於套件管理程式版本碰巧採用的預設值。這些設定會將延遲設為一星期、停用自動安裝指令碼,並為新增依賴項儲存確切版本。
npm 11.10.0+ — .npmrc:
# Minimum release age: 7 days
min-release-age=7
# Don't run scripts during installation
ignore-scripts=true
# Save exact versions without ^ or ~
save-exact=true
pnpm 11+ — pnpm-workspace.yaml:
# Minimum release age: 7 days, in minutes
minimumReleaseAge: 10080
# Fail if no eligible version is available
minimumReleaseAgeStrict: true
# Don't run scripts during installation
ignoreScripts: true
# Save exact versions without ^ or ~
saveExact: true
Yarn 4.12+ — .yarnrc.yml:
# Minimum release age: 7 days
npmMinimalAgeGate: "1w"
# Don't run install scripts from third-party dependencies
enableScripts: false
# Save exact versions without ^ or ~
defaultSemverRangePrefix: ""
這不會停用你自己的工作區中的指令碼。Yarn 説明文件。
Bun 1.3+ — bunfig.toml:
[install]
# Minimum release age: 7 days, in seconds
minimumReleaseAge = 604800
# Don't run scripts during installation
ignoreScripts = true
# Save exact versions without ^ or ~
exact = true
npm、pnpm 和 Bun 的範例會完全停用安裝指令碼。如果某個依賴項需要建置步驟,請透過套件管理程式設定選擇性核准,而非一律禁止。儲存確切版本也不會改寫包含 ^ 或 ~ 的現有項目。
將鎖定檔與設定一併提交,並在 CI 中使用相應的指令:
| 套件管理程式 | 鎖定檔 | CI 安裝 |
|---|---|---|
| npm | package-lock.json |
npm ci |
| pnpm | pnpm-lock.yaml |
pnpm install --frozen-lockfile |
| Yarn | yarn.lock |
yarn install --immutable |
| Bun | bun.lock |
bun ci |
惡意軟件檢查和 CI 權限需要分開設定。將依賴項更新放在各自獨立的 PR 中,並只將部署機密提供給負責部署的工作。
使用 Reforma 建立下一個網站。利用 AI 創作,並透過功能強大的視覺化網站建立工具全面掌控網站。設計每個版面配置、調整每個細節,並使用自己的網域發佈。
來源:DEV Community · dev.to