Web3.0安全开发实践:代理合约最佳实践总结

news2024/12/26 19:06:00

代理模式使智能合约能够升级其逻辑,同时维持其链上地址和状态值。对代理合约的调用会通过delegateCall的方式执行来自逻辑合约的代码,以修改代理合约的状态。

本文将为大家概述代理合约的类型相关的安全事件和建议,以及使用代理合约的最佳实践

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

我们都知道区块链的「不可篡改」特质,也说明了智能合约代码在部署于区块链上后就无法被修改。

因此当开发者想为逻辑升级、修复错误或因安全更新试图更新合约代码时,他们必须部署一个新的合约,并会生成一个新的合约地址。

想要解决这个问题,就可以用代理模式。

代理模式实现了合约的可升级性,并且不会改变合约的部署地址,这也是目前最普遍的合约升级模式。

代理模式是一个可升级的合约系统,包括代理合约和逻辑实现合约。

代理合约处理用户交互和数据及合约状态存储。用户对代理合约的调用会通过delegatecall()执行来自逻辑合约的代码,从而改变代理合约的状态。升级则是通过更新在代理合约预定存储槽里记录的逻辑合约地址来实现。

较为常规的三种代理模式分别是透明代理、UUPS代理和Beacon代理。

  • 透明代理

在透明代理模式中,升级功能是在代理合约中实现的。 代理合约的管理员角色被赋予了操作代理合约的直接权限,以更新代理对应的逻辑实现地址。没有管理员权限的调用者则会把他们的调用委托给实现合约。

注意:代理合约管理员不能是逻辑实现合约的关键角色,甚至也不能是普通用户,因为代理管理员无法与实现合约进行交互。

image.png

  • UUPS代理

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

image.png

  • Beacon代理

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

image.png

代理误用和安全事件

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

  • 代理管理的密钥泄露

代理管理员控制着透明代理模式的升级机制,如果管理员的私钥被泄露,攻击者可以升级逻辑合约并在代理状态上执行他们自己的恶意逻辑。

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的存储槽中,以防止在执行逻辑合约代码时发生冲突。

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

每个版本的逻辑合约都必须保持状态变量相同的顺序和类型,并且需要在现有变量的末尾添加新的状态变量。否则,委托调用会导致代理合约读取或覆盖不正确的存储值,而且旧的数据可能与新声明的变量相关联,这会给应用程序带来严重的问题。

image.png

③ 在基础合约中包含存储间隙

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

image.png

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

在声明期间或构造函数中分配状态变量只会影响逻辑合约中的值,不会影响代理合约。非不可变的参数应该使用initialize()函数来分配。

image.png

  • 合约继承(Contract Inheritance)

① 可升级合约只能继承自其他可升级合约

与不可升级的合约相比,可升级的合约具有不同的结构。例如,构造函数与改变代理状态不兼容,它使用initialize()函数来设置状态变量。任何继承另一个合约的合约都需要使用其继承合约的initialize()函数来分配各自的变量。当使用OpenZeppelin库或编写自己的代码时,确保可升级的合约只能继承其他的可升级合约

② 不要在逻辑合约中实例化新合约

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

image.png

③ 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()。虽然遵循最佳实践可以确保代理合约部署的稳定,同时保持可升级的灵活性,但所有代码都容易出现可能危及项目的新安全或逻辑问题,https://t.me/gtokentool 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2252042.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

第29天 MCU入门

目录 MCU介绍 MCU的组成与作用 电子产品项目开发流程 硬件开发流程 常用元器件初步了解 硬件原理图与PCB板 常见电源符号和名称 电阻 电阻的分类 贴片电阻的封装说明: 色环电阻的计算 贴片电阻阻值计算 上拉电阻与下拉电阻 电容 电容的读数 二极管 LED 灯电路 钳位作…

【人工智能基础05】决策树模型

文章目录 一. 基础内容1. 决策树基本原理1.1. 定义1.2. 表示成条件概率 2. 决策树的训练算法2.1. 划分选择的算法信息增益(ID3 算法)信息增益比(C4.5 算法)基尼指数(CART 算法)举例说明:计算各个…

数据结构与算法(排序算法)

我本将心向明月,奈何明月照沟渠。 排序的概念 1. 排序是指将一组数据,按照特定的顺序进行排列的过程。 2. 这个过程通常是为了使数据更加有序,从而更容易进行搜索、比较或其他操作。 常见的排序算法 插入排序 1. 把待排序的记录&#xff0c…

思科实现网络地址转换(NAT)和访问控制列表(ACL)和动态路由配置并且区分静态路由和动态路由配置。

实验拓扑(分为静态路由和动态路由两种) 静态路由互通 动态路由互通 实验背景 这个是想实现外网与内网的连接跟网络的探讨,最终实现互通以及使用并且在网络地址转换后能使用网络然后再这个基础上再配置访问控制列表和网络地址转换的的学习过程。 实验需了解的知识…

开发一套ERP 第八弹 RUst 插入数据

更全面的报错,方便检查错误在哪里,现代高级语言越来越智能 还是得看下原文档怎么操作的 src 目录为crate 的根目录 想在crate 中模块相互引入需要在 main 中声明,各个模块,然后才能在各个模块中相互引入和使用 原始工程引入,避免直接使用 lib.rs 回合cargo 中的一些 工程管理出…

课程答疑微信小程序设计与实现

私信我获取源码和万字论文,制作不易,感谢点赞支持。 课程答疑微信小程序设计与实现 摘要 随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了课程答疑微信小程序设计与实现的开发全过程。通过分析…

【时间之外】IT人求职和创业应知【53】-东莞也转型

目录 新闻一:Freysa挑战赛:人类智慧与策略战胜AI,奖金高达4.7万美元 新闻二:中国生成式AI用户规模突破2.3亿,行业应用广泛 新闻三:2024东莞智能终端新技术推广会圆满举行,聚焦AI与智能终端融…

ARP欺骗-监控网络

kali: 使用arp-scan -l 查看同个局域网 windows arp -a 查看地址的物理地址 192.168.21.2 对应的是00-50-56-f5-d5-f0 攻击利用: 我们要让目标ip的流量经过我的网卡,从网关出去 使用的开启 echo 1 > /proc/sys/net/ipv4/ip_forward 当为0时,我们不转发&…

Cesium 当前位置矩阵的获取

Cesium 位置矩阵的获取 在 3D 图形和地理信息系统(GIS)中,位置矩阵是将地理坐标(如经纬度)转换为世界坐标系的一种重要工具。Cesium 是一个强大的开源 JavaScript 库,用于创建 3D 地球和地图应用。在 Cesi…

网络编程项目1

基于TCP通信控制红色机械臂和蓝色机械臂的运作 1、项目要求&#xff1a;通过w&#xff08;红色臂角度增大&#xff09;s&#xff08;红色臂角度减小&#xff09;d&#xff08;蓝色臂角度增大&#xff09;a&#xff08;蓝色臂角度减小&#xff09; #include <myhead.h> #…

使用GitZip for github插件下载git仓库中的单个文件

背景&#xff1a;git仓库不知道抽什么疯&#xff0c;下载不了单个文件&#xff0c;点击下载没有反应&#xff0c;遂找寻其他方法&#xff0c;在这里简单记录下。 使用GitZip for github插件下载仓库中的单个文件 1、首先在浏览器安装插件&#xff0c;并确保为打开状态。 2、然…

Y20030022基于PHP+MYSQL疫苗预约管理网站的设计与实现 源码 初稿

旅游度假区微信小程序 1.摘要2.研究背景和意义3. 系统功能4.界面展示5.源码获取 1.摘要 疫苗预约管理系统是为了提供一个高效、便捷、安全的平台&#xff0c;方便用户进行疫苗预约接种&#xff0c;并优化疫苗接种的管理工作。通过该系统&#xff0c;用户可以随时随地进行预约操…

C#中判断两个 List<T> 的内容是否相等

ET实现游戏中邮件系统逻辑思路&#xff08;服务端&#xff09;_游戏邮件系统设计-CSDN博客 场景&#xff1a;今天遇到一个BUG&#xff0c;在服务器重启的时候&#xff08;体验服&#xff09;&#xff0c;玩家之前接收的邮件又重新接收了一次&#xff0c;但是两封邮件的ID是不同…

算法编程题-优势洗牌

算法编程题-优势洗牌 原题描述方法一、排序二分查找思路简述代码实现复杂度分析 方法二、红黑树思路简述代码实现复杂度分析 方法三、贪心思路简述代码实现复杂度分析 摘要&#xff1a;本文将对LeetCode原题优势洗牌进行介绍&#xff0c;从最容易想到的方法开始&#xff0c;循序…

【从零开始的LeetCode-算法】3264. K 次乘运算后的最终数组 I

给你一个整数数组 nums &#xff0c;一个整数 k 和一个整数 multiplier 。 你需要对 nums 执行 k 次操作&#xff0c;每次操作中&#xff1a; 找到 nums 中的 最小 值 x &#xff0c;如果存在多个最小值&#xff0c;选择最 前面 的一个。将 x 替换为 x * multiplier 。 请你…

ERROR in [eslint] Invalid Options ‘extensions‘ has been removed.

看着这个报错 感觉是版本不对引起的 ERROR in [eslint] Invalid Options: - Unknown options: extensions - extensions has been removed. ERROR in Error: Child compilation failed: [eslint] Invalid Options: - Unknown options: extensions - extensions has b…

SpringCloud书单推荐

重新定义SpringCloud实战 129 疯狂SpringCloud微服务架构实战 SpringBootSpringCloud微服务开发实战 点餐系统 SpringCloud微服务架构实战派 日访问量3000W平台 SpringCloud Alibaba微服务原理与实战 Spring CloudNginx 极简spring cloud实战 Spring Cloud 微服…

试题转excel;试题整理工具;试卷转excel;word转excel

一、问题描述 我父亲是一名教师&#xff0c;偶尔会需要将试卷转excel&#xff0c;方便管理处理一些特别重要的题目 于是&#xff0c;就抽空写一个专门将试题转excel的工具&#xff0c;便于各位教师从业者和教育行业的朋友更好的整理试题&#xff0c;减少一点重复枯燥的工作 …

Node.js Fastify装饰器:提升你的API性能与功能

在Node.js的世界中&#xff0c;Fastify以其卓越的性能和插件化架构脱颖而出&#xff0c;成为构建高效API的首选框架之一。Fastify的装饰器功能&#xff0c;允许开发者以声明式的方式增强和扩展核心对象&#xff0c;如请求&#xff08;Request&#xff09;和响应&#xff08;Res…

数据库管理-第267期 23ai:Oracle Data Redaction演示(20241128)

数据库管理267期 2024-11-286 数据库管理-第267期 23ai&#xff1a;Oracle Data Redaction演示&#xff08;20241128&#xff09;1 示例表及数据2 创建编校策略2.1 名字全编校2.2 电话部分编校 3 DML演示3.1 场景13.2 场景2 总结 数据库管理-第267期 23ai&#xff1a;Oracle Da…