币灵灵财经
首页 > 币圈新闻 > 文章正文

打破区块链的不可篡改性:代理模式如何实现智能合约升级?

币灵灵财经 2024-11-01 01:36 1256

欧易交易所

欧易交易所

软件大小:268.26MB

软件版本:v3.4.2

可升级的合约和代理模式简介

透明代理

打破区块链的不可篡改性:代理模式如何实现智能合约升级?

UUPS代理

打破区块链的不可篡改性:代理模式如何实现智能合约升级?

Beacon代理

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

打破区块链的不可篡改性:代理模式如何实现智能合约升级?

代理误用和安全事件

开发人员可以利用代理模式合约来实现可升级的合约系统。然而,代理模式也有一定的操作门槛,如果使用不当,可能会给项目带来堪称毁灭性的安全问题。以下部分展示了与代理误用相关的事件,以及代理带来的中心化风险。

代理管理的密钥泄露

2021年3月5日,PAID Network遭受了由于私钥管理不善而引起的“铸币”攻击。PAID Network被攻击者利用,攻击者窃取了代理管理员的私钥并触发了升级机制以更改逻辑合约。

未初始化的UUPS代理实现

initialize()函数通常被“initializer”修饰符保护,以限制该函数只能被调用一次。在调用initialize()函数后,从代理合约的角度看,逻辑合约被初始化了。

逻辑合约被接管的影响取决于系统中的合约代码。在最坏的情况下,攻击者可以将UUPS代理模式中的逻辑合约升级为恶意合约,并执行“自毁”函数调用,这可能导致整个代理合约变得毫无用处,合约中的资产将永久丢失。

① Parity Multisig Freeze:未初始化逻辑合约。攻击者触发了许多钱包的初始化,并通过调用 selfdestruct() 将以太币锁定在合约中。

② Harvest Finance、Teller、KeeperDAO和Rivermen都使用了未初始化的逻辑合约,这将允许攻击者任意设置合约的初始化参数,并在delegatecall() 期间执行selfdestruct()将代理合约销毁。

OpenZeppelin库提供的代理合约不会在合约中声明状态变量,而是基于EIP 1967标准,将需要存储的数值(如管理地址)保存到特定的存储槽中,以防止冲突。

项目方私自定义的proxyAdmin的值被错误的当成了initialized和initializing的值,使得initializer修饰符返回了错误的结果,进而允许攻击者再次调用initialize()函数并将管理合约的权限授予自己。攻击者随后更改了投票参数并通过了他们的恶意提案,以窃取Audius资产。

Pickle Finance、Furucombo以及dYdX攻击事件。

(1)仅在必要的时候才使用代理模式

(2)不要修改代理库

(1)初始化逻辑合约

攻击者可以接管一个未初始化的逻辑合约,并有可能借此破坏代理合约系统。因此请在部署后初始化逻辑合约,或者在逻辑合约的构造函数中使用_disableInitializers()来自动禁用初始化。

(2)确保代理管理账户的安全

一个可升级的合约系统通常需要一个“代理管理员”的特权角色来管理合约的升级。如果管理密钥泄露,攻击者可以自由地将合约升级为恶意合约,就可以窃取用户的资产。我们建议谨慎管理代理管理账户的私钥,以避免任何被黑客攻击的潜在风险。可以使用多签名钱包来防止单点的密钥管理失败。

(3)为透明代理管理使用单独的账户

(1)在代理合约中声明状态变量时要小心谨慎

(2)维护逻辑合约的变量声明顺序和类型

打破区块链的不可篡改性:代理模式如何实现智能合约升级?

(3)在基础合约中包含存储间隙

逻辑合约需要在合约代码中包含存储间隙,以便在部署新的逻辑实现时预测到新的状态变量。在添加新的状态变量后,需要适当地更新间隙的大小。

打破区块链的不可篡改性:代理模式如何实现智能合约升级?

(4)不在构造函数或声明过程中设置状态变量的值

打破区块链的不可篡改性:代理模式如何实现智能合约升级?

合约继承

(1)可升级合约只能继承自其他可升级合约

与不可升级的合约相比,可升级的合约具有不同的结构。例如,构造函数与改变代理状态不兼容,它使用initialize()函数来设置状态变量。

(2)不要在逻辑合约中实例化新合约

通过Solidity创建实例化的合约将不可升级。合约应单独部署,并将其地址作为参数传递给可升级的逻辑合约,以实现可升级状态。

打破区块链的不可篡改性:代理模式如何实现智能合约升级?

(3)Parent合约初始化风险

逻辑合约的实现

避免使用selfdestruct()或对不信任的合约执行selegatecall()/call()

写在最后

逻辑实现合约也应注意不要使用delegatecall(),这样可以防止攻击者执行某些恶意代码,如selfdestruct()。