找回密码
 立即注册
查看: 24|回复: 0

打破区块链的弗成 修改 ,署理 模式如何以最佳方法 实现智能合约升级?

[复制链接]

9401

主题

0

回帖

2万

积分

论坛元老

积分
28211
发表于 2024-3-1 12:58:00 | 显示全部楼层 |阅读模式
免责声明:本文旨在传递更多市场信息,不组成 任何投资建议。文章仅代表作者不雅 点,不代表MarsBit官方立场。
小编:记得存眷 哦
来源:CertiK
原文题目 :打破区块链的弗成 修改,署理 模式如何以最佳方法 实现智能合约升级?
署理 模式使智能合约能够升级其逻辑,同时维持其链上地址和状态值。对署理 合约的挪用 会通过delegateCall的方法 执行来自逻辑合约的代码,以修改署理 合约的状态。本文将为年夜 家概述署理 合约的类型、相关的平安 事件和建议,以及使用署理 合约的最佳实践。
可升级的合约和署理 模式简介
我们都知道区块链的「弗成 修改」特质,也说明了智能合约代码在安排 于区块链上后就无法被修改。
因此当开发者想为逻辑升级、修复毛病 或因平安 更新试图更新合约代码时,他们必须安排 一个新的合约,并会生成一个新的合约地址。
想要解决这个问题,就可以用署理 模式。
署理 模式实现了合约的可升级性,并且不会转变 合约的安排 地址,这也是目前最普遍的合约升级模式。
署理 模式是一个可升级的合约系统,包含 署理 合约和逻辑实现合约。
署理 合约处理用户交互和数据及合约状态存储。用户对署理 合约的挪用 会通过delegatecall执行来自逻辑合约的代码,从而转变 署理 合约的状态。升级则是通过更新在署理 合约预定存储槽里记录的逻辑合约地址来实现。
较为通例的三种署理 模式分别是透明署理 、UUPS署理 和Beacon署理 。
透明署理
在透明署理 模式中,升级功能是在署理 合约中实现的。署理 合约的治理 员角色被付与 了操作署理 合约的直接权限,以更新署理 对应的逻辑实现地址。没有治理 员权限的挪用 者则会把他们的挪用 委托给实现合约。
注意:署理 合约治理 员不克不及 是逻辑实现合约的症结 角色,甚至也不克不及 是普通用户,因为署理 治理 员无法与实现合约进行交互。

UUPS署理
在UUPS(Universal Upgradeable Proxy Standard)模式中,合约升级功能是在逻辑合约中实现的。由于升级机制存储在逻辑合约中,升级事后 的版本可以删除升级相关的逻辑,以禁止未来的升级。在这种模式下,所有对署理 合约的挪用 都邑 被转发给逻辑实现合约。

Beacon署理
Beacon署理 模式允许多个署理 合约通过引用Beacon合约来共享同一个逻辑实现。Beacon合约为挪用 的署理 合约提供逻辑实现合约的地址,当升级到一个新的逻辑实现地址时,只需要更新Beacon合约里记录的地址即可。

署理 误用和平安 事件
开发人员可以利用署理 模式合约来实现可升级的合约系统。然而,署理 模式也有一定的操作门槛,如果使用欠妥 ,可能会给项目带来堪称毁灭性的平安 问题。以下部分展示了与署理 误用相关的事件,以及署理 带来的中心化风险。
署理 治理 的密钥泄露
署理 治理 员控制着透明署理 模式的升级机制,如果治理 员的私钥被泄露,进击 者可以升级逻辑合约并在署理 状态上执行他们自己的恶意逻辑。
2021年3月5日,PAID Network遭受了由于私钥治理 不善而引起的“铸币”进击 。PAID Network被进击 者利用,进击 者窃取了署理 治理 员的私钥并触发了升级机制以更改逻辑合约。升级后,进击 者可以销毁用户的PAID,并为自己铸造了一批PAID,随后再将其出售。代码自己 并不存在平安 漏洞,而是进击 者从治理 员那里获取了升级合约的私钥。
未初始化的UUPS署理 实现
对于UUPS署理 模式,在署理 合约的初始化进程 中,初始参数由挪用 者传给署理 合约,再由署理 合约挪用 逻辑合约里的initialize函数实现初始化。
initialize函数通常被“initializer”修饰符掩护 ,以限制该函数只能被挪用 一次。在挪用 initialize函数后,从署理 合约的角度看,逻辑合约被初始化了。然而,从逻辑合约的角度来看,逻辑合约并没有被初始化,因为initialize并没有在逻辑合约中被直接挪用 。鉴于逻辑合约自己 未被初始化,任何人都可以挪用 initialize函数来初始化它,将状态变量设置为一个恶意的值,并有可能接管逻辑合约。
逻辑合约被接管的影响取决于系统中的合约代码。在最坏的情况下,进击 者可以将UUPS署理 模式中的逻辑合约升级为恶意合约,并执行“自毁”函数挪用 ,这可能导致整个署理 合约变得毫无用处,合约中的资产将永久丧失 。
案例
① Parity Multisig Freeze:未初始化逻辑合约。进击 者触发了许多钱包的初始化,并通过挪用selfdestruct 将以太币锁定在合约中。
② Harvest Finance、Teller、KeeperDAO和Rivermen都使用了未初始化的逻辑合约,这将允许进击 者任意设置合约的初始化参数,并在delegatecall 期间执行selfdestruct将署理 合约销毁。
存储冲突
在一个可升级的合约系统中,署理 合约不会声明状态变量,而是使用伪随机存储槽来存储重要数据。

署理 合约将逻辑合约状态变量的值保存在它们被声明的相对位置。如果署理 合约声明了它自己的状态变量,这时署理 和逻辑合约都试图使用同一个存储槽,就会产生 存储冲突。
OpenZeppelin库提供的署理 合约不会在合约中声明状态变量,而是基于EIP 1967标准,将需要存储的数值(如治理 地址)保存到特定的存储槽中,以防止冲突。

案例
北京时间2022年7月23日,去中心化音乐平台Audius遭到黑客进击 ,该事件是由于在署理 合约中引入新的逻辑,从而产生存储冲突所导致的。
署理 合约声明了一个proxyAdmin地址状态变量,在执行逻辑合约代码时,其值会被毛病 地读取。
项目方私自界说 的proxyAdmin的值被毛病 的当成了initialized和initializing的值,使得initializer修饰符返回了毛病 的结果,进而允许进击 者再次挪用 initialize函数并将治理 合约的权限授予自己。进击 者随后更改了投票参数并通过了他们的恶意提案,以窃取Audius资产。
在逻辑合约里挪用 delegatecall或不信任的合约
假设delegatecall存在于一个逻辑合约中,而合约没有正确验证挪用 目标。在这种情况下,进击 者可以利用该函数来执行对其控制的恶意合约的挪用 ,以破坏逻辑实现或执行自界说 逻辑。
同样地,逻辑合约中若有一个不受限制的address.call函数,一旦进击 者恶意提供地址和数据字段,就可以利用其充当署理 合约。
案例
Pickle Finance、Furucombo以及dYdX进击 事件。
在这几起事件中,存在漏洞的合约获得了用户token的批准,并且合约内存在一个由用户提供挪用 合约地址和数据的call/delegatecall,进击 者将能够挪用 具有transferFrom功能的合约,以提取用户余额。在dYdX事件中,dYdX执行了他们自己的白帽进击 以掩护 资金。
最佳实践
一般情况
①仅在需要 的时候才使用署理 模式
不是每个合约都需要升级。如上文所示,使用署理 模式涉及很多风险。“可升级”的属性也会引起信任问题,因为署理 治理 员可以在没有获得 社区同意的情况下升级合约。我们建议仅在需要 时才将署理 模式整合到项目中。
② 不要修改署理 库
署理 合约库很庞杂 ,尤其是处理存储治理 和升级机制的部分。修改中的任何毛病 都邑 影响署理 和逻辑合约的工作。我们在审计进程 中发明 的年夜 量与署理 有关的高严重性bug都是由不正确的署理 库修改造成的。Audius事件就是一个典范 的例子,展示了署理 合约修改欠妥 所带来的后果。
署理 合约操作治理 要点
① 初始化逻辑合约
进击 者可以接管一个未初始化的逻辑合约,并有可能借此破坏署理 合约系统。因此请在安排 后初始化逻辑合约,或者在逻辑合约的结构 函数中使用_disableInitializers来自动禁用初始化。
② 确保署理 治理 账户的平安
一个可升级的合约系统通常需要一个“署理 治理 员”的特权角色来治理 合约的升级。如果治理 密钥泄露,进击 者可以自由地将合约升级为恶意合约,就可以窃取用户的资产。我们建议谨慎治理 署理 治理 账户的私钥,以避免任何被黑客进击 的潜在风险。可以使用多签名钱包来防止单点的密钥治理 失败。
③为透明署理 治理 使用零丁 的账户
署理 治理 和逻辑治理应该是自力 的地址,以防止丧失 与逻辑实现的交互。如果署理 治理 和逻辑治理引用同一个地址,就不会转发任何挪用 来执行特权功能,从而禁止治理功能的更改。
署理 合约存储相关
①在署理 合约中声明状态变量时要小心谨慎
正如Audius黑客事件中所解释的那样,署理 合约在声明自己的状态变量时必须谨慎。在署理 合约中以正常方法 声明的状态变量会在读写数据时造成数据冲突。如果署理 合约需要一个状态变量,请将该值保存在一个类似EIP1967的存储槽中,以防止在执行逻辑合约代码时产生 冲突。
② 维护逻辑合约的变量声明顺序和类型
每个版本的逻辑合约都必须坚持 状态变量相同的顺序和类型,并且需要在现有变量的末尾添加新的状态变量。不然 ,委托挪用 会导致署理 合约读取或笼罩 不正确的存储值,并且 旧的数据可能与新声明的变量相联系关系 ,这会给应用法度模范 带来严重的问题。

③ 在基础合约中包含存储间隙
逻辑合约需要在合约代码中包含存储间隙,以便在安排 新的逻辑实现时预测到新的状态变量。在添加新的状态变量后,需要适本地 更新间隙的年夜 小。

④ 不在结构 函数或声明进程 中设置状态变量的值
在声明期间或结构 函数中分派 状态变量只会影响逻辑合约中的值,不会影响署理 合约。非弗成 变的参数应该使用initialize函数来分派 。

合约继承(Contract Inheritance)
① 可升级合约只能继承自其他可升级合约
与弗成 升级的合约相比,可升级的合约具有不合的结构。例如,结构 函数与转变 署理 状态不兼容,它使用initialize函数来设置状态变量。任何继承另一个合约的合约都需要使用其继承合约的initialize函数来分派 各自的变量。当使用OpenZeppelin库或编写自己的代码时,确保可升级的合约只能继承其他的可升级合约。
② 不要在逻辑合约中实例化新合约
通过Solidity创建实例化的合约将弗成 升级。合约应零丁 安排 ,并将其地址作为参数传递给可升级的逻辑合约,以实现可升级状态。

③ Parent合约初始化风险
当初始化Parent合约时,__{ContractName}_init函数将初始化其Parent合约。挪用 多个 __{ContractName}_init可能导致Parent合约的第二次初始化。注意__{ContractName}_init_unchained将只初始化{ContractName}的参数,而不会挪用 其Parent合约的初始化器。
然而,这并不是一个值得推荐的做法,因为所有的Parent合约都需要被初始化,不初始化所需的合约会导致以后的执行问题。
逻辑合约的实现
① 避免使用selfdestruct或对不信任的合约执行delegatecall/call。
如果合约中存在selfdestruct或delegatecall,进击 者就有可能利用这些函数来破坏逻辑实现或执行自界说 逻辑。开发者应验证用户的输入,不允许合约执行对不信任合约的delegatecall/calls。此外,不建议在逻辑合约中使用delegatecall,因为在多个合约的署理 链中治理 存储结构 会很麻烦。
写在最后
署理 合约通过使协议在安排 后能够更新其代码逻辑,来绕过区块链的弗成 修改特质。然而,开发署理 合约依旧需要十分谨慎,不正确的实现可能会导致项目平安 与逻辑问题。
总的来说,最佳实践是使用权威提供的且经过普遍 测试的解决计划 ,因为透明、UUPS和Beacon署理 模式每个都有针对各自用例的经过验证的升级机制。除此之外,升级署理 的特权角色也应该被平安 地治理 ,以防止进击 者转变 署理 逻辑。
逻辑实现合约也应注意不要使用delegatecall,这样可以防止进击 者执行某些恶意代码,如selfdestruct。
虽然遵循最佳实践可以确保署理 合约安排 的稳定,同时坚持 可升级的灵活性,但所有代码都容易涌现 可能危及项目的新平安 或逻辑问题。因此所有的代码最好由如CertiK这般的具备审计和掩护 署理 合约协议经验的平安 专家团队进行适当的审计。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|货拉客微商论坛 |网站地图|网站地图

GMT+8, 2024-9-22 15:44 , Processed in 0.078822 second(s), 19 queries , Gzip On.

Powered by Huolake! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表