我们需要担心以太坊的开发依赖吗?|GCC Research
WAGMI
随机数
Bybit
最近的 Bybit 被黑事件可以被分类为一种供应链攻击的事件。
撰文:shew
概述
由于 Web3 行业对于开源与共享的价值观提倡,开源软件已成为加密货币行业的基石,开源项目的协作模式推动了技术创新的指数级增长。然而,这种高度互联的生态也催生了一个关键挑战:供应链攻击正在成为悬在开源世界头顶的"达摩克利斯之剑"。攻击者通过入侵核心依赖项、污染代码仓库或劫持维护者账户,能够在数小时内引发蝴蝶效应般的系统性风险,威胁整个生态的稳定性。
最近的 Bybit 被黑事件 可以被分类为一种供应链攻击的事件。Bybit 的事后报告指出 Bybit 出现问题的根本原因是朝鲜黑客集团向 Safe 的前端系统内注入了恶意代码导致 Bybit 签名人看到签名内容与实际内容不符。此前,就出现过 Ledger connect-kit 被注入恶意代码的情况,虽然最终黑客只获得了 600 美元。这起事件产生的原因是 Ledger connect-kit 被恶意发布了新的包含恶意代码的版本。
这些事件说明:大量公共基础设施类项目(如底层协议、核心工具链)因其不可替代性,逐渐形成“单点故障”的依赖黑洞——当这些关键节点出现安全缺口时,依赖它们的数百个下游项目可能瞬间暴露于攻击之下,形成技术多米诺骨牌效应。
本研究聚焦于以太坊的 Typescript 生态的开源供应链体系,详细分析了以太坊开发中核心组件库的依赖问题,核心目标是分析可能的风险传导链。
GCC 希望报告可以缓解以太坊用户对于以太坊生态供应链安全的顾虑。以太坊生态的核心组件并没有复杂的依赖问题。但同时我们也需要指出 wagmi 等前端组件为了兼容各种钱包而导入了各钱包供应商的 SDK,这导致以太坊前端开发还是存在一定的供应链问题。GCC 呼吁钱包供应商对 EIP-6963 兼容而不是开发 SDK。
前置条件
由于作者的视野限制,本文只会讨论 Typescript 和 JavaScript 生态下的开发。大部分开发者和用户都只会接触到 Typescript 编写的程序。但是在以太坊的基础设施层,比如数据检索等,存在相当多的开发者使用 Golang 和 Rust 开发工具的情况。但是这部分开发者往往都是与数据检索等内容打交道,大部分情况下并不会触碰到终端用户的私钥等。
本文主要内容集中于 Typescript 生态下的讨论,有以下几个原因:
- 目前开发除合约外的以太坊应用首选原因基本就是 Typescript。背后的深层原因可能是因为 Typescript 可以更好的与 ABI 结合,开发者体验非常好
- Typescript 编写的代码往往用于前端,这部分代码会直接与用户接触
- Typescript 语言开发往往会引入大量第三方库,一直以来都存在严重的供应链安全问题
- 笔者较为熟悉 Typescript 开发生态系统
除此外,本文讨论的是 现代 以太坊开发。从目前来看,使用 ether.js 可能已经不能被称为现代以太坊开发,现代以太坊开发的基石应该是围绕着 viem 展开的生态系统。本文着重分析的就是 viem 的开发依赖问题。
wagmi
wagmi 是目前以太坊最主流的前端框架,作为一个前端框架,wagmi 具有相当多的依赖,但 wagmi 的核心依赖可以只有 2 个:
- @wagmi/core 一个几乎没有依赖的核心功能库,该库并不负责任何以太坊交互,主要功能是一些框架功能
- @wagmi/connectors 一个具有相当复杂依赖的库,该库主要负责钱包连接
为了实现广泛的钱包连接,wagmi 在 @wagmi/connectors 内导入了来自不同钱包供应商的 SDK。大部分钱包供应商 SDK 都有自己的依赖,这导致 @wagmi/connectors 具有非常复杂的依赖。下图展示了依赖关系的冰山一角:
这些复杂的依赖带给了 wagmi 较大的供应链风险。理论上,这些钱包 SDK 都是由安全意识较强且值得信赖的钱包供应商提供,但是还是存在可能性被攻击,如上图中,wagmi 依赖了 @safe-global/safe-apps-sdk,在过去,我们可能认为来自多签钱包 safe 的 SDK 几乎没有被黑的可能性,但是最近的事件指出 Safe 可能并没有足够的安全意识。
在此处,我们需要指出目前以太坊已经确定了 EIP-6963 规范了钱包的交互行为。而 wagmi 等框架也是对 EIP-6963 进行了完整支持。钱包供应商可以考虑直接兼容 EIP-6963,以避免用户前端对于钱包供应商的 SDK 导入。
假如开发者不信任 Wagmi,那么开发者是否可以构建以太坊应用呢?事实是可以的,但是一旦不使用 wagmi 等前端框架,开发者很难构造出有良好前端的应用,最终构造出的产品形态可能更加类似命令行工具。
viem
在早期前端开发中,@jxom (jxom.eth) 和 @tmm (awkweb.eth) 主导开发的 wagmi 使用了 ether.js 作为底层库实现与以太坊的交互。但 wagmi 的开发者发现了 ether.js 的一系列问题,随后就自己开发了 viem。
viem 的功能包含了 ether.js 的全部功能,即使用 viem 可以实现连接钱包和发起合约调用等功能。同时提供了一些对开发者更友好的特性,如:
- ABI 的 Typescript 类型绑定,一旦开发者在进行合约调用时传入了与 ABI 类型不相符的参数则会直接出现类型错误
- 更高的运行效率
- 更少的依赖和打包后更低的体积
viem 的开发者在开发不久后就获得了 Paradigm 的资助,这些资助使得 @jxom 和 @tmm 全职为 viem 工作。当然,Paradigm 凭借着其技术影响力也不断向社区推荐 viem。在 viem 开发不久后,wagmi 使用的底层库默认为 viem。
下图展示了 viem 的依赖项。
viem 的依赖项是十分精简的,其中 isows 是 viem 作者自己开发的与 WebSocket 服务器交互的组件。而 ws 也是一个非常著名的 WebSocket 组件。
我们可以看到 viem 最核心的依赖是 ox 库,我们会在后文更加详细的介绍这个库的供应链问题。ox 是 viem 开发团队在开发了 viem 一段时间后,决定将 viem 中与链底层相关的代码进一步模块化,由此产生了 ox 项目。该项目也是 viem 开发团队维护。
其他的依赖还包括 @noble/hashes 密码学库,事实上上图中所有的 @noble 和 @scure 开头的库都是同一个开发者开发的密码学库,这些库都是经过审计的,我们会在后文进一步讨论这些密码学库。
我们认为 viem 团队做恶的概率并不大,相比于其他开源项目,viem 开发者是全职工作的,而且依靠大量捐赠,viem 开发者没有充分的利益动机做恶。更加重要的一点是 viem 是开源的,一旦出现恶意代码,极大概率会被发现。
但是如果我们要求极高安全性,viem 依旧是一个可能出现问题的库,核心原因在于 viem 内部存在一些与钱包交互以及 RPC 交互有关的代码,这些代码在用户使用热钱包时可能会进行一些操作。假如 viem 被注入恶意代码,有机会出现用户私钥被窃取的情况。
ox
假如我们不需要 viem 帮助我们与钱包交互,而是只需要一个库生成一些交易数据等,那么我们可以使用上文介绍的 viem 依赖 ox。相比于 veim,ox 是一个更加底层的库,该库的主要功能就是进行生成交易或者签名的数据。该库内也包含一些与签名有关的代码,但是这些代码都不会与用户的热钱包或者冷钱包交互,都是需要用户手动输入私钥才可以。从不包含钱包交互角度来看,ox 库理论上更加安全。
ox 的依赖图如下:
此处依赖的 @adraffy/ens-normalize 的功能是 ENS 用户名规范化。而 abitype 是一个用于 ABI 类型校验的库,该库实际上只会在开发阶段使用。而 eventemitter 是一个相当简单的只有 200 行代码的库。其他库都是密码学库。
假如读者手中持有巨额资金并且担心自己遭受供应链攻击,那么最简单的方案就是在一个离线设备中使用 ox 库为自己的交易生成交易的原始数据,然后通过其他方式将生成的交易原始数据发送到冷钱包内签名,然后继续使用 ox 将签名与交易原始数据拼接生成最终的交易数据,最后将最终的交易数据广播到以太坊网络内。
密码学库
在上文中,我们介绍的所有组件实际上都依赖于密码学库,密码学库会提供哈希以及椭圆曲线等功能。在一些 极其复杂的攻击 中,攻击者可以通过构建恶意的签名数据窃取密钥。比如在签名过程中使用低熵值的随机数,用户签名并将交易广播后,黑客可以使用用户的交易签名数据爆破计算用户私钥。但是对于使用正版硬件钱包和钱包固件的用户来说并不需要担心此问题,理论上钱包固件开发者会妥善处理签名过程中的随机数生成。
viem 和 ox 依赖的密码学库,包括 @scure/base 和 @noble/hashes 等库其实都是paulmillr 编写的。
paulmillr 是一个应用密码学家,非常擅长密码学库等编写和优化。同时 paulmillr 作为密码学家也非常重视供应链安全问题,他编写的所有库都是没有依赖的 ( 存在部分库依赖自己之前编写的基础库 )。而且拥有一套严格的抗供应链攻击的规则:
最重要的是,paulmillr 编写的库几乎都是存在审计的。这些审计来自不同的安全公司,以 @noble/curves 为例:
我们可以看到来自不同组织资助的审计。
当然,paulmillr 编写的所有库都会存在 Security 一栏提醒该库存在的一些安全问题。比如所有库中的随机数都来自 crypto.getRandomValues 方法。该方法理论上是密码学安全的,但其具体实现由 Javascript 运行时实现的,在过去曾经出现过浏览器实现不当的情况。
最大的问题可能就是 paulmillr 被威胁导致密码学库被推入恶意代码,但是这种问题可以通过开发者锁定版本解决,开发者应该只依赖审计过的版本。
总结
至此,本文就简单引领读者观察了以太坊生态系统内的核心开发依赖。在实际情况下,开发者只使用 viem 实际上就可以构建出一个基本的命令行应用。我们可以看到以太坊的 TypeScript 生态系统中的依赖是很低的,大部分库都是无依赖的,最基础的密码学库也是经过审计的。
但是我们也观察到 wagmi 作为最常用的前端框架存在一系列对其他钱包供应商 SDK 的依赖,这意味着这些使用 wagmi 的前端应用是存在一定的被供应链攻击的风险的。这也是为什么相当多的安全人员都认为前端应用可能并不适合超大规模资金的操作,以及提倡离线优先原则。
另外,本文内所有的依赖关系图都是由 npmgraph 生成。
免责声明:
1.资讯内容不构成投资建议,投资者应独立决策并自行承担风险
2.本文版权归属原作所有,仅代表作者本人观点,不代表Bi123的观点或立场