Web3中的供应链攻击——从NPM到协议漏洞利用

  • zealynx
  • 发布于 2 天前
  • 阅读 50

本文分析了Web3项目JavaScript供应链安全的五大攻击向量:恶意npm包发布(2025年9月18个包影响20亿周下载量)、维护者账户被攻破(@solana/web3.js和dYdX事件)、依赖混淆攻击、包名钓鱼(如solana-py)以及CI/CD注入(构建工具篡改最终代码)。文章强调,即使智能合约审计完美,前端构建管线被攻破也会导致资金被盗。提供了详细检查清单,包括锁定版本、使用私有注册表、实施可重复构建等防御措施。

摘要 — 导致 Web3 项目失血的 5 大向量

  • 恶意包发布劫持钱包交易(npm 2025:18 个包,20 亿周下载量)
  • 维护者账号被盗毒化官方库(@solana/web3.js、@dydxprotocol/v4-client-js,约 5000 万月下载量)
  • 通过包名混淆的供应链攻击欺骗构建系统安装恶意包(DeFi 中的理论风险)
  • 利用包名进行钓鱼攻击,通过拼写错误窃取加密货币(solana-py、truffle-vscode)
  • CI/CD 注入在打包过程中注入恶意代码——源代码保持干净,最终产物被武器化

你的 JavaScript 依赖项是直接访问用户资金的通道。你的构建流水线是铸造信任——或者摧毁信任的熔炉。


现实:供应链攻击就是 DeFi 攻击

2024 年,23 起针对加密货币的供应链攻击中,有 14 起针对 npm( The Hacker News)。在 2025 年 9 月的攻击中,Wiz 报告称约 10% 的分析云环境中至少包含一个被攻陷的包( Wiz.io 分析)。

这对 Web3 来说改变了一切:

  • 前端打包的 JavaScript 在用户钱包中执行
  • 恶意代码拦截钱包调用并在签名前修改交易接收方
  • 构建流水线(CI/CD)自动从被攻陷的源拉取更新
  • 攻击者专门针对加密基础设施

本文通过真实事件和实用检查清单,分解了五个关键攻击向量。


1. 恶意包发布:npm 大规模攻陷(2025 年 9 月)

发生了什么:2025 年 9 月 8 日,威胁行为者通过 npmjs.help 钓鱼攻击维护者 Josh Junon,并在 2 小时内发布了 15 多个广泛使用的包的恶意版本:

debug@4.4.2, chalk@5.6.1, ansi-styles@6.2.2, strip-ansi@7.1.1, color-convert@3.1.1, wrap-ansi@9.0.1 等

这些包合计每周下载量超过 20 亿次。

运作方式:攻击者拦截了浏览器 API(fetchXMLHttpRequest)和钱包调用(window.ethereum.request、Solana 签名)。恶意软件扫描区块链地址(ETH、BTC、SOL、LTC、BCH、TRX)并将交易接收方修改为攻击者钱包,使用相似地址以逃避检测。

影响:

  • 任何打包这些版本的前端都会向用户提供恶意代码
  • 恶意交易在用户察觉之前就被签名
  • 团队不得不暂停发布、重建和审计依赖项
  • 攻击扰乱了整个生态系统的开发流水线

成功原因:

  • 受信任的维护者账号——npm 用户自动信任这些包
  • 极高的下载量——这些工具无处不在
  • 混淆技术将恶意代码隐藏在合法功能中

2. 维护者账号被盗:官方库被毒化

当攻击者获得合法包的发布凭证时,他们绕过了所有的信任假设。两个主要事件展示了这种模式:

@solana/web3.js 后门(2024 年 12 月)

2024 年 12 月 2 日,攻击者使用被盗的维护者账号发布了 @solana/web3.js 的恶意版本(v1.95.6、v1.95.7)。这是官方的 Solana JavaScript 库,月下载量约 5000 万,依赖项目超过 3000 个。

后门函数 addToQueue 将私钥外泄到 sol-rpc[.]xyz。ReversingLabs 的分析发现 14 个修改过的文件包含 C2 网址( ReversingLabs 研究)。GitHub 安全公告:"任何安装或运行此包的计算机都应被视为完全被攻陷。"

与钓鱼攻击的关键区别:

  • 攻击者控制了合法的包——无需拼写错误
  • 每个自动更新工具(Dependabot、npm-check-updates)都面临风险
  • 影响范围巨大——可能影响数千个 dApp

出了什么问题:社会工程攻击攻陷了维护者的发布账号。恶意代码存活了 5.5 小时后才被检测并移除。

经验教训:

  • 即使是官方、维护良好的库也可能被武器化
  • 信任必须持续验证——监控维护者活动,强制执行 2FA,使用硬件密钥
  • 生产环境锁定确切版本并手动审查所有依赖变更
  • 应急响应窗口至关重要——每一小时都很关键

实际影响:应急响应团队报告称在巨大压力下争分夺秒地隔离受影响的机器、强制轮换密钥并从干净缓存重建。一个 DeFi 团队不得不在安全公告发布后一小时内从三台机器上转移密钥。

dYdX 入侵:协同账号接管(2026 年 1 月)

2026 年 1 月 27 日,攻击者使用被盗的发布凭证发布了 @dydxprotocol/v4-client-js(npm)和 dydx-v4-client(PyPI)的恶意版本。这是对官方 dYdX 开发者账号的直接账号接管。

攻击者在 npm 上发布了版本 3.4.11.22.11.15.21.0.31,在 PyPI 上发布了 1.1.5post1——所有版本看起来都像是官方维护者的合法更新。

npm 载荷:钱包窃取器嵌入在 registry.ts 中,将种子短语外泄到 dydx.priceoracle[.]site

PyPI 载荷:钱包窃取器 + 远程访问木马(RAT)用于任意代码执行

dYdX 的回应:"任何安装了这些包的计算机都应被视为被攻陷"来源)——详见 Socket.dev 的技术分析( Socket.dev 分析)。

关键洞察:两次攻击都展示了同样的模式——当攻击者拥有密钥时,他们不需要漏洞。dYdX 事件显示了升级:同时攻陷两个生态系统(npm + PyPI)并带有复杂的载荷。


3. 通过包名混淆的供应链攻击

机制

由 Alex Birsan 发现,依赖混淆利用了包管理器解析内部依赖与公共依赖的方式:

  1. 公司使用内部包 @company/auth(私有仓库)
  2. 包名通过 GitHub、配置文件或前端产物泄露
  3. 攻击者将 @company/auth 以更高版本号发布到公共 npm
  4. 构建系统选择公共包 → 执行攻击者代码

为什么 DeFi 容易受到攻击:

  • 内部包名(@yourprotocol/cli)通常在前端产物中暴露
  • CI/CD 系统自动从公共仓库拉取最新版本
  • 大多数团队使用 pip --extra-index-url(添加公共索引)而非 --index-url(仅替换为私有索引)
  • 高价值目标:处理交易签名或钱包管理的包

根本原因:包管理器无论来源如何都选择最高版本(pip --extra-index-url 行为,某些 Artifactory 虚拟仓库配置)。

修复:

  • 在公共仓库中预留内部包名以防止外部发布
  • pip:使用 --index-url(仅指向私有索引,绝不使用 --extra-index-url
  • npm:分离内部和外部仓库;不要混合在一个虚拟仓库中
  • 扫描公共仓库以查找泄露的内部包名

注意:虽然目前没有大型 DeFi 协议公开确认过依赖混淆入侵,但攻击面巨大且理论上可被利用。


4. 通过包名的钓鱼攻击

2025 年 9 月的 npm 攻击始于 npmjs.help——一个用于钓鱼维护者的域名。这种攻击之所以成功,是因为:

  1. 高风险环境——开发人员在压力下工作(代币发行、审计截止日期)
  2. 复杂的包名——@solana/web3.jsethers-tsviem-utils
  3. 跨生态系统差异——solana(PyPI) vs solana-py(GitHub)
  4. 新人涌入——许多开发人员不熟悉确切的包名

近期 Web3 示例:

  • solana(PyPI) vs solana-py(GitHub)——恶意 Solana Python API 窃取加密密钥( Sonatype 报告
  • truffle-vscode vs truffle-for-vscode——从钱包开发人员处窃取密钥( Mend.io
  • bip39 变种——针对 BIP39 钱包生成器

防御措施:

  • 直接从官方文档复制粘贴包名
  • 始终验证:下载量、维护者、发布日期、已验证发布者徽章
  • 使用作用域包(@solana/web3.js)——更难伪造
  • 实现预安装 Hook,检查包是否在已知的拼写错误黑名单中
  • 将包验证纳入开发人员入职和代码审查流程

5. CI/CD 注入:隐形威胁

机制

构建时依赖注入发生在构建工具链本身被攻陷时——不是包,而是产生最终 JavaScript 产物的工具。目标包括:

  • 打包器/构建工具:Webpack、Vite、Rollup、esbuild
  • 转译器:Babel、TypeScript 编译器(tsc)、SWC
  • 压缩器:Terser、UglifyJS
  • 构建插件:vite-plugin-web3、webpack 插件、Rollup 插件
  • 构建脚本:postinstallprepareprepublish Hook

运作方式:

  1. GitHub 上的源代码 100% 干净
  2. 构建过程运行被攻陷的工具/插件
  3. 恶意代码注入到最终的 dist/bundle.js
  4. 部署的前端提供武器化的产物
  5. 仓库审计什么也发现不了——攻击仅存在于构件中

为什么 Web3 尤其容易受到攻击:

DeFi 前端执行关键操作:

  • 生成签名的交易
  • 调用 window.ethereum.request()
  • 构造 swap/approval 的 calldata
  • 设置授权额度

一个被攻陷的打包器可以:

  • 替换接收方地址
  • 将授权从 router 改为 attacker
  • 增加额度(type(uint256).max
  • 将合法 calldata 替换为抽干函数

具体示例(AST 操作):

// 源代码中的原始内容:
ethers.sendTransaction({ to: router, value: amount });

// 恶意插件将其转换为:
ethers.sendTransaction({ to: attacker, value: type(uint256).max });

你审查的代码(src/)不是用户执行的代码(dist/)。

现实中的类似案例:

  • ua-parser-js——在发布流水线中插入后门
  • SolarWinds——构建系统被攻陷,在签名更新中注入恶意代码

为什么在 Web3 中几乎没人谈论这个:

大多数审计关注的是:

  • 智能合约 ✓
  • npm 包 ✓
  • 钱包钓鱼 ✓

但前端构建完整性很少被验证。结果是:经过完美审计的合约被抽干,因为前端构造了恶意 calldata。

缓解措施:

  • 可重现构建:git clone → npm ci → npm build 必须在不同机器上产生相同的哈希值
  • 构件验证:仅当 hash(bundle) == expected_hash 时部署(在 CI 中存储哈希,发布前验证)
  • 签名发布:使用 Sigstore/Cosign/GPG 签名最终产物;在部署流水线中验证签名
  • 无动态脚本:在生产包中避免使用 postinstallprepareprepublish Hook
  • 不可变 CI 运行器:使用临时的、新预配的运行器进行构建;切勿重用被攻陷的运行器
  • 构建溯源:记录构建环境(依赖项、工具版本、命令)以供取证重现

结论:你的构建流水线是你的攻击面的一部分

数字不会说谎:

  • 18 个 npm 包在 2 小时内被武器化(20 亿周下载量)
  • @solana/web3.js——约 5000 万月下载量,3000 多个依赖项,私钥窃取
  • dYdX 因账号接管被攻陷——发布凭证被盗
  • 2024 年,23 起加密供应链攻击中有 14 起针对 npm( The Hacker News
  • 构建流水线被攻陷——源代码干净,最终产物被武器化

旧模式:"审计合约,忽略前端"——已过时

新现实:每个依赖项和每个构建工具都是潜在的交易劫持向量

即使经过完美审计的智能合约,如果前端构建流水线被攻陷,也可能被抽干。安全必须超越合约,延伸到整个软件供应链。


检查清单:立即保护你的供应链

从这里开始:检查你的直接暴露

grep -E "debug|chalk|ansi|@solana/web3.js" package-lock.json

然后运行这份完整检查清单:

  • 锁定确切版本——从 package.json 中移除浮动版本(^~)。在锁定文件中使用确切版本。
  • 扫描未知包——检查 package-lock.json / yarn.lock 中是否有你不认识的包。
  • 审计内部名称泄露——搜索 GitHub 仓库和前端产物中可能已公开的内部包名。
  • 验证关键依赖项——确保 @solana/web3.jsetherswagmiviem 处于未受攻陷的版本。
  • 启用子资源完整性(SRI)——为 CDN 提供的 JavaScript 产物添加 integrity 哈希值。
  • 阻止依赖自动更新——配置 CI/CD,要求生产环境依赖更新必须手动批准。
  • 使用私有仓库——Verdaccio、Nexus 或 Artifactory,配合黑名单和白名单。
  • 事件后清除缓存——在所有开发机器和 CI 运行器上执行 npm cache clean --force
  • 正确配置包管理器——pip 使用 --index-url(绝不使用 --extra-index-url);npm 分离仓库;不要混合内部和外部源。
  • 强制使用作用域包——所有内部代码必须使用 @yourorg/* 命名。
  • 监控维护者账号活动——为关键维护者账号的发布事件设置警报。
  • 验证仓库配置——确保 npm/yarn/pip 在适用时只配置使用私有仓库。
  • 实现可重现构建——验证 git clone → npm ci → npm build 在干净环境中产生相同的产物哈希值。
  • 构件验证——在 CI 中存储产物哈希;仅当哈希匹配预期值时部署。
  • 签名发布——使用 Sigstore/Cosign/GPG 签名最终产物;在部署流水线中验证签名。
  • 避免动态构建脚本——从生产包中移除 postinstallprepareprepublish
  • 使用不可变 CI 运行器——为每次构建预配新的运行器;绝不重用可能被攻陷的运行器。
  • 记录构建溯源——记录工具版本、依赖项和命令,以便取证重现。

如果任何一项检查失败,请停止下一次发布并立即修复。


联系我们

在 Zealynx,我们审计全栈 Web3 安全——智能合约、前端和供应链。因为用户的资金依赖于每一个层面。

准备好保护你的构建流水线了吗?通过 zealynx.io/quote 联系我们进行全面的供应链审查。

欲了解更多影响 DeFi 的 Web2 基础设施风险,请查看我们关于 DNS 劫持和前端攻陷 的文章。


常见问题

  1. npm audit 能检测到这些攻击吗?

    不能。供应链攻击的速度太快,漏洞数据库跟不上。你需要主动监控,而不仅仅是 CVE 扫描。

  2. 如何知道自己是否已被攻陷?

    搜索受影响版本的构建日志。扫描已部署的 JavaScript 产物中已知的恶意模式。检查暴露窗口期间链上交易是否有意外的接收方变更。

  3. 我是否应该停止使用 npm?

    不应该——但请使用不可变构建和带有严格白名单的私有仓库。风险在于自动更新,而非包管理器本身。

  4. Yarn 和 pnpm 安全吗?

    如果配置不当,它们仍然容易受到包混淆攻击。问题在于仓库配置,而非客户端。

  5. GitHub Dependabot 呢?

    如果维护者账号被攻陷,它可能会引入恶意代码。锁定版本,并手动审查所有生产版本的依赖更新。

  6. 锁定文件能保护我吗?

    它们可以防止意外升级,但如果开发者本地缓存已被攻陷,锁定文件也无济于事。在任何安全事件后,务必从干净缓存进行全新安装。


术语表

术语 定义
供应链攻击 对受信任的软件组件(库、包、构建工具)的攻陷,该攻陷会级联影响多个组织。
钓鱼攻击 社会工程技术,通过相似的包名或域名欺骗用户安装恶意包或泄露凭证。
子资源完整性(SRI) 安全特性,验证加载的 JavaScript 文件是否与加密哈希匹配,以检测篡改。

查看完整术语表 →

  • 原文链接: zealynx.io/blogs/Supply-...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
zealynx
zealynx
江湖只有他的大名,没有他的介绍。