年内第二起:NPM 再遭污染,一文读懂供应链攻击

今年 9 月 8 日开始,NPM 就没真正安静过。
第一起事件发生在 9 月份。知名维护者 qix 的账号被钓鱼邮件盗取,攻击者利用他的身份向多个高下载量的代码包发布恶意版本,累计影响每周超过二十亿次的下载量,恶意代码会在用户进行加密货币操作时悄悄替换交易信息。
这周,第二起事件出现。安全团队披露了 Shai-Hulud 2.0 攻击,攻击者同样通过盗取维护者账号,将恶意安装脚本写入数百个包,不仅通过散播恶意代码感染开发者用户窃取本地环境中的密钥、令牌信息,并通过提权和 Runner 潜伏在本级中伺机而动,同时还将对开发者云端托管的CI/CD流程进行攻击,窃取CI/CD中的各种凭证信息。
此外,病毒会在Github仓库创建恶意Workflow 待后续利用。
两起事件的出现,把一个问题摆到台面上:
NPM 供应链攻击到底在攻击什么?为什么一个恶意版本,就能引起整个生态的连锁风险?
一、什么叫 NPM 供应链攻击?
想象一下:App Store 上某个知名开发者的账号被黑了。
黑客用这个账号发了一次「正常更新」,但更新包里多了一段恶意代码。
用户看见新版本上线了,也没感受到哪里不对,就习惯性点「更新」。
结果成千上万台手机中毒。
NPM 供应链攻击就是这个逻辑:
- NPM 作为“开发者的 App Store”
- 里面的“应用”是各种代码包(库)
- 所有前端 / Node.js / Web3 项目都在用
这意味着,风险来自于一条链路,开发者默认信任上游的包,默认更新版本是安全的。
二、年内两起 NPM 上游污染,分别干了什么?
1. 第一回:9 月 8 日 qix 事件——劫持浏览器里的加密交易
这次被攻击的是维护者 Josh Junon(ID:qix)。
过程非常经典:
- 黑客伪装成 npm 官方,发一封「2FA 要过期」的钓鱼邮件;
- 维护者在压力之下按提示操作,把二次验证也一并交出去;(Security Boulevard)
- 攻击者接管账号,开始给多个热门包偷偷发恶意更新。
恶意代码干的事情很直接:
- 只要用户在浏览器里发起加密货币交易
- 恶意脚本就拦在中间,把收款地址换成黑客的
- 页面上显示的是“正常的地址”,实际发出去的是另一串
因为这些包的下载量实在太大,这次攻击也被不少安全报告称为 NPM 生态史上影响面最大的一次供应链事件之一。(Cyberint)
2. 第二回:Shai-Hulud 2.0 —— 从“劫持交易”升级成“挖空整个开发环境”
9 月中旬开始,安全社区第一次发现名为 Shai-Hulud 的 NPM 蠕虫:
它会利用被攻陷的维护者账号,把恶意代码塞进 NPM 包里,在安装阶段执行脚本,窃取开发者和 CI/CD 环境中的密钥。(wiz.io)
到了 11 月 24 日左右,第二波,也就是 Shai-Hulud 2.0 被多家安全团队确认:
- 至少 七八百个 NPM 包被植入恶意安装脚本;(securitylabs.datadoghq.com)
- 安装这些依赖时,脚本会自动执行,收集环境变量、云密钥、CI/CD 令牌;
- 窃取到的秘密会被推到攻击者控制的 GitHub 仓库中,并用 “Shai-Hulud” 做标记。(wiz.io)
跟 9 月 8 日那次相比,这一波的区别在于:目标从“偷用户的钱”,升级成了“挖开发者和企业整套基础设施的钥匙”。
第一起更多偏前端劫持,直接对终端用户下手;
第二起则更像是对整个开发、运维体系下手,是一场真正意义上的“生态级渗透”。
三、为什么 Web3 对这类攻击尤其敏感?
NPM 出问题,受伤的不只是 Web3,Web2、SaaS、CI/CD 全部都会中枪,这是事实。
但 Web3 有几个特别脆弱的点:
- 私钥就是钱
很多项目习惯把部署私钥、管理密钥写进 env 或 CI/CD 配置里,一旦被拿走就是资产损失,而不是“数据泄露慢慢处理”。 - 前端一旦被投毒,交易可以被“无感替换”
攻击者可以在你点“确认”之前把收款地址、合约参数换掉,表面看起来一切正常。 - 工具链高度依赖 Node.js / NPM
Hardhat、Foundry 的周边工具、web3.js、ethers.js 生态,大量依赖 NPM 包;
Shai-Hulud 这类蠕虫专门在这种基础设施层做文章。
对 Web2 来说,供应链攻击意味着数据泄露或服务异常,通常可修复。
对 Web3 来说,供应链攻击直接触及密钥和链上资产,属于不可逆风险。
四、NPM 供应链攻击到底在攻击什么?
如果只用三个词总结它:
- 账号:维护者账号被钓鱼、被撞库、被社会工程。
- 发布链:恶意 PR 被合入正常版本进行推送。
- 信任:下游项目默认相信上游已经帮你“审过代码”。
也就是说,它不是技术栈某一个 bug,而是利用了整条链上的一个“默认假设”:
“只要是熟悉的包、新版本,就可以放心更新。”
前两起事件已经证明,这个默认假设本身就是最大的风险。
五、OneKey 的应对措施
对于这种“上游污染型”的攻击,OneKey 做的事情很简单:把“默认信任”拆掉。
1. App 侧:不盲目进行上游更新
OneKey 在依赖管理上有几个决定性做法:
- 使用锁版本(lockfile),构建只认明确指定的版本,不会自动升级到“刚发出来的最新版”;
- 任何依赖升级都需要走固定的代码审查和指纹校验流程;
- 安全团队会针对关键依赖单独复核,避免把“供应链里还没看明白的东西”放进来。
这意味着:
哪怕上游被投毒了,只要我们没手动升级到那个版本,就不会中招。
2. 硬件钱包侧:本地独立解析,让前端攻击失效
就算某个 Dapp 前端被 NPM 供应链攻击污染了,只要你用的是 OneKey 硬件钱包:
- 交易数据会在本地芯片里重新解析;
- 设备屏幕上会显示真实的收款地址、金额、合约方法、授权对象;
- 你是在设备上确认“真实数据”,而不是在浏览器里点一个“看上去没问题的按钮”。
前端如果被改成“把钱转给黑客”,你在硬件屏幕上能直接看到:
地址不对、金额不对、合约名字不对。
六、如果你是开发者,可以立刻做的几件事
不管你是不是钱包项目,至少可以立刻检查这三点:
- 把依赖锁死
使用 package-lock.json / pnpm-lock.yaml,不要在生产构建中使用“自动拉最新”。 - 把密钥挪出 env 和 CI
特别是私钥、助记词这类,全部改为用硬件钱包或专门的签名服务。 - 关闭“没必要的 postinstall”
CI 中禁止执行不必要的workflow和workflow内的安装脚本,减少“安装时自动执行恶意代码”的空间。
这些都不是高深技巧,只是把“默认信任”改成“默认怀疑”。
七、写在最后
今年的两起 NPM 上游污染事件说明,供应链并不会一直保持安全状态。
即便是高下载量、广泛使用的依赖,只要维护者账号出现问题,就可能立即影响整条链路。
我们无法控制上游,但可以控制自己使用代码的方式。
减少自动更新、加强审查流程、确保关键行为只在受信环境中进行,是目前最可靠的应对方式。
让依赖链「清晰可控」,是比「事后修补」更有效的长期策略。






