免责声明:本文旨在在不构成任何投资建议的情况下传达更多的市场信息。本文仅代表作者的观点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黑客事件中解释的代理合同在声明其状态变量时必须谨慎。在代理合同中以正常方式声明的状态变量会导致读写数据数据冲突。如果代理合同需要状态变量,请将该值保存在类似的状态变量中EIP在执行逻辑合同代码时,应在1967的存储槽中防止冲突。
② 维护逻辑合同的变量声明顺序和类型
每个版本的逻辑合同必须保持相同的顺序和类型的状态变量,并需要在现有变量的末尾添加新的状态变量。否则,委托调用将导致代理合同读取或覆盖不正确的存储值,旧数据可能与新声明的变量有关,这将给应用程序带来严重问题。
③ 存储间隙包含在基本合同中
逻辑合约需要在合同代码中包含存储间隙,以便在部署新的逻辑实现时预测新的状态变量。添加新的状态变量后,需要适当更新间隙的大小。
④ 状态变量值不在构造函数或声明过程中设置
状态变量在声明期间或构造函数中的分配只会影响逻辑合同中的值,而不会影响代理合同。应使用不可变的参数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这样一计和保护代理合同协议经验的安全专家团队适当的审计。
今天的文章就分享到这里,如果你也网赚副业项目感兴趣,可以添加 维信:beng6655 备注:副业!
如若转载,请注明出处:https://www.haoxue2.com/19640.html