文章探讨了智能合约的可升级性,对比了以太坊、Solana、Cosmos和Substrate等不同区块链生态系统中实现升级的不同模式,并分析了各种升级模式下的安全风险,例如访问控制不当、状态损坏、合约崩溃和治理漏洞,最后给出了安全升级的建议。
智能合约曾经因其不可变性而备受赞誉。 代码一旦部署就永远无法更改的想法既是一个特性,也是一个缺陷。 随着协议的成熟和用户需求的发展,开发人员意识到冻结逻辑的局限性。 可升级性成为了答案。 但这是有代价的。 每一种升级机制都引入了一个新的复杂性层、一类新的错误和一个新的攻击面。
在过去的几年里,我们一直在审计以太坊、Solana、Cosmos 和其他生态系统中的智能合约。 一个反复出现的主题是开发人员尝试使其合约更灵活的创造性(有时是危险的)方式。 这篇博客深入探讨了现存的不同可升级性模式、它们在底层是如何实现的以及它们在哪里出错。
不可变性在理论上听起来很完美。 但无论你的智能合约写得多么好,都会出现 Bug。 业务逻辑会演变。 治理会发生变化。 如果没有升级的方法,不可变合约中的一个小小错误可能会永远锁定数百万美元。 另一方面,升级的能力可能会破坏对去中心化的信任,并为 rug pulls、后门或意外的 bricking 打开大门。
找到正确的平衡是关键。 但要实现这一目标,你需要了解可升级性的设计空间。
基于代理的模式
以太坊和其他 EVM 链中最广泛采用的方法是代理委托。 在这里,用户与代理合约交互,该合约使用 delegatecall 将逻辑执行转发到单独的实现合约。
有一些常见的变体:
透明代理
使用管理帐户来授权升级。 只有管理员可以调用升级函数; 其他调用被转发到逻辑合约。 这里最大的风险是意外地将升级函数暴露给公众或搞砸存储槽。
UUPS(通用可升级代理标准)
实现合约包含升级逻辑,而不是依赖外部管理代理。 它更节省 gas 且更模块化,但如果升级授权逻辑没有得到适当保护,也会更危险。
信标代理
专为扩展用例而设计,其中多个代理共享相同的实现逻辑。 信标合约存储当前逻辑合约的地址。 当它更新时,所有代理都会自动指向新的实现。 由于共享逻辑风险,这种模式更难安全地管理。
存储布局陷阱
EVM 环境中的可升级合约需要精确控制存储布局。 代理和逻辑合约的变量之间的任何不匹配都可能导致灾难性的损坏。 Solidity 不强制执行布局一致性,因此一个错放的变量可能会悄无声息地破坏一切。
Solana 智能合约(称为程序)被编译为 BPF 字节码,并使用指定的“升级权限”部署到链上。 如果此帐户持有必要的密钥,则可以随时部署该程序的新版本。
虽然 Solana 的升级模型在某些方面更简单,但它带来了中心化风险。 如果升级权限是单个热钱包,则整个合约可以被任何入侵者替换。 我们已经看到项目推迟去中心化太久,而且有些项目从未撤销其权限。 这就失去了使用 Solana 安全、高性能运行时的全部意义。
Solana 还引入了围绕程序派生地址 (PDA) 的风险。 一些开发人员在升级过程中不正确或不一致地使用 bumps,导致状态冲突或丢失对关键数据帐户的访问权限。
使用 CosmWasm 构建的 Cosmos 合约允许通过迁移入口点进行显式迁移。 这种模式的优点在于升级逻辑是合约本身的一部分。 当通过链上治理提议并批准升级时,新的 WASM 字节码会被上传,并且执行会继续。
由于状态会自动保留并且迁移逻辑是显式定义的,因此存储不匹配的风险较低。 但是,不正确的迁移代码或治理层中的权限错误仍然可能导致问题。
我们看到的一个常见问题是迁移逻辑中缺乏验证; 合约通常假设存储中存在某些值而不进行检查,从而导致升级后出现 panic 或破坏不变量。
在 Polkadot 和其他基于 Substrate 的链中,可以通过治理提案来交换整个运行时。 这允许强大且协调的升级,但引入了复杂性。 你不是升级单个合约,而是升级整个链逻辑。
对于用 Ink! 编写的合约(Substrate 的智能合约语言),升级不是原生的。 通常,你需要部署一个新合约并手动迁移状态,除非你已经构建了自定义升级机制。 这为开发人员提供了灵活性,但将安全负担完全放在他们身上。
无论底层链是什么,风险都属于几个关键类别:
2022 年,一个主要的以太坊 DeFi 协议推送了一次升级,该升级无意中覆盖了关键的存储变量,导致数十亿美元的 TVL 被冻结,直到部署了热修复程序。
一个基于 Solana 的项目失去了对其 PDA 金库的访问权限,因为它在不同版本中错误地计算了 bumps。
在 Cosmos 中,配置错误的迁移逻辑导致升级后某些用户的余额归零,从而迫使链停止并进行手动恢复。
每个生态系统都有自己的故事。 而共同的主题始终是一样的:可升级性功能强大,但默认情况下很少安全。
作为安全研究人员,我们倾向于将可升级性视为一种特权、危险的操作。 如果你必须使用它,我们建议你这样做:
最重要的是:制定回滚计划。 即使你认为升级是万无一失的。
可升级性不是区块链系统中的缺陷。 它在许多生产协议中是必需的。 但它也是合约可能拥有的最危险的功能之一。 无论你是在以太坊中使用 delegatecall,在 Solana 中升级权限,还是在 Cosmos 中迁移 hooks,有一件事仍然成立:你正在用责任换取不可变性。
这不仅仅是编写可升级的合约。 而是设计不会危及用户信任、去中心化或安全性的升级路径。
因此,真正的模式不仅仅是代码,而是谨慎。
- 原文链接: blog.immunebytes.com/202...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!