Npm 遭遇史上最大攻擊!26 億次下載包遭殃,結果竟然只損失 20 美元?
9 月 8 日,npm,這個前端人每天都要打交道的包管理器,遭遇了史上最大規模的供應鏈攻擊。
受影響的包,下載量高達 26 億/周!而且中招的很多包,咱們可能都聽說過:
- chalk:命令行彩色日志
- debug:瀏覽器、Node.js 調試必備
- ansi-styles:終端樣式的處理
這些庫幾乎滲透進了所有前端項目。換句話說,只要你寫過 npm install,就可能踩坑。
更離譜的是,這次攻擊的起點居然只是一封釣魚郵件。
圖片
翻譯文本:
嗨,qix! 作為我們持續致力于賬戶安全的一部分,我們請求所有用戶更新他們的 雙因素認證 (2FA) 憑證。我們的記錄顯示,自您上次更新 2FA 以來,已經超過 12 個月。
為了維護您賬戶的安全性和完整性,我們懇請您盡快完成此次更新。請注意,從 2025 年 9 月 10 日 開始,所有 2FA 憑證過期的賬戶將被 臨時鎖定,以防止未經授權的訪問。
立即更新 2FA
如果您有任何問題或需要幫助,我們的支持團隊隨時可以為您提供協助。您可以通過此指定的鏈接。 聯系我們。
翻起來非常正常的一段話,同時也正因為如此才會導致該包的維護者一時大意,點開了假冒的“npm 官方支持”鏈接,結果賬號被盜,黑客隨即往這些高頻依賴里塞進惡意代碼。
圖片
攻擊手法解密
這次的網絡攻擊,并不是常見的木馬或者刪除文件的病毒,而是一個專門針對 加密貨幣Crypto Clipper(一種專門盯著“加密貨幣轉賬”的惡意軟件,可以偷偷替換錢包地址)。
它的工作方式很隱蔽:通過 在代碼里加入了大量混淆后的 JS。 其中函數名會被替換成一堆毫無意義的字符。比如這樣
const _0x112fa8=_0x180f;(function(_0x13c8b9,_0_35f660){const _0x15b386=_0x180f,_0x66ea25=_0x13c8b9();while(!![]){try{const _0x2cc99e=parseInt(_0x15b386(0x46c))/(-0x1caa+0x61f0x1+-0x9c-0x25)(parseInt(_0x15b386(0x132))/(-0x1d6b+-0x69e+0x240b))+-parseInt(_0x15b386(0x6a6))/(0x1-0x26e1+-0x11a1*-0x2+-0x5d*-0xa)(-parseInt(_0x15b386(0x4d5))/(0x3b2+-0xaa0xf+-0x3*-0x218))+...
在被植入的代碼中,黑客主要做了兩件事:
- 攔截網絡請求:尤其是和錢包相關的請求(如 MetaMask、Ethereum、Solana)。
- 替換轉賬地址:利用 Levenshtein 算法,生成一個和真實地址幾乎一模一樣的假地址,用戶在瀏覽器里根本察覺不到。
舉個例子: 你以為要轉賬到:
1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2但代碼會偷偷替換成:
1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN3(最后的 2 變成了 3)只差一個字符,錢就直接打到黑客錢包里了。
一次構建失敗導致事件曝光
如果這場攻擊完全潛伏下來,那么后果不堪設想。
但萬萬沒想到的是,最先把黑客拽出來的,并不是一些所謂的安全專家,而是 一次意外的構建失敗。
在某個團隊的 CI/CD 管道里,日志突然報了一個異常:
ReferenceError: fetch is not defined一開始,大家都以為是 Node.js 環境太舊,缺少全局 fetch,屬于常規兼容性問題。
可細查之下,發現問題并不簡單:這次錯誤來自一個名不見經傳的小依賴包:error-ex。
繼續往下扒源碼,結果發現了咱們上一段文章中所描述的那段內容了...
圖片
不過,這些混淆之后的代碼雖然看不同,但是里面卻有一個函數名是 checkethereumw(檢查以太坊)。
那么到這里,謎底就已經揭曉了:
黑客早就在這些依賴里埋下了 Crypto Clipper(加密剪輯器) 的代碼,只是正好因為這個團隊的 Node 版本很老,沒有
fetch,惡意代碼沒跑通,反而意外暴露了問題。
整個問題復盤:npm install 的坑
其實對于所有前端人來說,都會面臨一個非常嚴重的問題,那就是 巨大的 node-modules。
是時候再次拿出這張圖啦...
一個非常普通的項目,依賴包可能也會有 幾十,甚至 上百 個。
而我們平時在執行 npm install 或 pnpm install 時,其實就相當于是在拉一個巨大的“依賴森林”。
比如說:
我們只安裝一個 axios,但是你實際下載之后會發現它背后可能還會帶上五六個工具庫。以下是我基于 vite@5 剛創建完成的 Vue 項目,就會默認有如此多的依賴。
圖片
這意味著什么?
這就意味著,其實我們 絕大多數開發者對自己的依賴樹一無所知。
你以為是自己寫的項目,其實 90% 的代碼都是別人寫的。這些“別人寫的代碼”,只要有一個環節被污染,就可能讓整個項目淪陷。
這次攻擊就是最好的例子。攻擊者沒有去動 Vue、React 這種“大明星”,而是盯上了 chalk、strip-ansi 這種深埋在依賴樹底層的“小透明”。
結果就導致一大堆項目被牽連,目前所牽連的包總的周下載量,已經達到了 26 億次/周。
給大家列覺幾個例子
- chalk:每周下載量約 3 億次
- strip-ansi:每周約 2.6 億次
- color-convert:每周約 1.9 億次
- color-name:每周約 1.9 億次
- error-ex:每周約 4700 萬次
- simple-swizzle:每周約 2600 萬次
- has-ansi:每周約 1200 萬次
這次事件的后果
這次事件聽上去好像很嚴重,但是有意思的是 黑客最終只騙到大約 20 美元的加密貨幣(也有人查看鏈之后,計算黑客騙到了大約 400 美元)。
為啥?
原因就在于 社區反應太快了:
- 9 月 8 日 13:16 UTC:黑客開始推送帶毒版本
- 1 小時后:有開發者在構建日志里發現異常,立刻在社區發出預警
- 不到 2 小時:npm 安全團隊和作者協同完成清理,惡意版本被全面下架
短短兩小時,全球開發者齊心協力,硬是把這場本可能釀成“生態級災難”的攻擊扼殺在搖籃里。
雖然損失不大(黑客只賺了 20 美元,連一頓正經燒烤都不夠),但是范圍很廣(波及 26 億/周下載量的依賴)
同時也提醒我們,需要注意一些防御措施。
比如:
- 審查依賴樹:打開
package-lock.json或pnpm-lock.yaml,檢查有沒有中招的版本 - 使用 npm audit:掃描項目的依賴樹,檢測是否包含已知的安全漏洞或惡意代碼
- 直接鎖定依賴版本:在
package.json里明確寫死安全版本
之前可能沒有同學會特別在意這些事。但是,除了這個事情之后,可能后面很多大廠可能都會去做一次自檢了...
































