代理安全检查清单:33项关键可升级性检查

  • zealynx
  • 发布于 2026-02-13 19:33
  • 阅读 28

本文详细介绍了以太坊虚拟机(EVM)代理和可升级性的安全检查清单,强调了在构建可升级合约时可能出现的五种关键失败情况,包括存储冲突、未受保护的初始化器、实现自毁、函数选择器冲突和升级授权绕过。文章还提供了涵盖33个安全检查项的清单,旨在帮助开发者和审计人员在开发和部署代理合约时避免常见的安全漏洞,确保用户资金安全。

过于冗长;不看版 —— 5个导致你的代理合约被黑的点

构建可升级合约?以下是导致超过 3.5 亿美元代理合约漏洞利用的关键失败点:

  1. 存储冲突 —— 新变量覆盖现有状态,破坏用户数据或资金
  2. 未受保护的初始化器 —— 任何人都可以重新初始化合约并成为管理员
  3. 实现合约自毁 —— 销毁实现合约会破坏所有使用它的代理合约,使其报废
  4. 函数选择器冲突 —— 代理合约函数会遮蔽实现合约函数,破坏访问控制
  5. 升级授权绕过 —— 弱管理员控制允许未经授权的实现更改

此清单涵盖 8 个领域中的 33 项安全检查。在审计之前、开发期间以及作为所有代理合约实现发布前的关卡使用它。


🔐 交互式清单可用

我们创建了此清单的交互式版本,其中包含可展开的详细信息、代码示例和审计复选框。非常适合审计员和开发团队。

→ 查看 EVM 代理和可升级性安全清单


简介:为什么代理合约安全至关重要

可升级的智能合约已成为 DeFi 协议的必备组件,这些协议需要在部署后进行迭代、修复错误和添加功能。代理模式通过将合约逻辑(实现)与状态(代理)分离来实现这一点,从而允许逻辑更新,同时保留用户数据和资金。

但这种能力带来了非凡的复杂性和风险。

数字说明了一切:

  • DeFi 中因代理合约相关漏洞损失超过 3.5 亿美元
  • 在分析的 3,671 个代理合约审计问题中,发现了 770 多个升级问题
  • 564 个初始化问题 —— 未受保护或缺失的初始化器
  • 176 个存储冲突 —— 跨实现的布局损坏
  • 15% 的发现为高危+级别,对协议安全性产生严重影响

代理模式是最容易被利用的智能合约机制之一,因为它们违反了 Solidity 的许多安全性假设。与简单的合约不同,代理合约必须管理:

  • 代理合约和实现合约之间的跨合约状态一致性
  • 跨升级的存储布局兼容性
  • 没有构造函数的初始化安全性
  • 代理合约和实现合约之间的函数选择器路由
  • 升级授权和治理机制

本清单是通过分析领先公司的 50 多份代理合约审计报告,并结合 DeFi 中主要代理合约漏洞利用的事后分析而提炼出来的。无论你是实现 OpenZeppelin 的可升级合约、构建自定义代理模式,还是审计可升级系统,本指南都将帮助你避免成为下一个受害者。

如何使用此清单

此清单分为八个关键领域:

每个部分包括:

  • 为什么重要 —— 背景和真实漏洞利用示例
  • 安全检查 —— 要在实现中验证的具体项目
  • 危险信号 —— 指示漏洞的警告标志
  • 代码模式 —— 安全与不安全的实现示例

对于使用特定代理模式的团队,我们提供了针对 UUPS 代理透明代理钻石模式 的专门指导。

1. 存储和布局安全

为什么重要

存储冲突是代理合约漏洞的首要原因。当代理合约和实现合约使用重叠的存储槽时,升级可能会静默覆盖关键数据,如用户余额、所有权或协议参数。其影响通常是灾难性的且无法挽回。

与其他智能合约错误不同,存储冲突几乎不可能通过测试检测到,因为它们可能仅在特定升级序列后才 проявляться。到那时,用户资金已经丢失。

安全检查

ERC-1967 合规性

  • 实现槽正确 —— 使用 bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) 作为实现地址存储
  • 管理员槽正确 —— 使用 bytes32(uint256(keccak256('eip1967.proxy.admin')) - 1) 作为管理员地址存储
  • 信标槽正确 —— 对于信标代理,使用 bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)
  • 保留槽受到尊重 —— 永远不要使用低于 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc 的槽

存储间隙管理

  • 所有可升级合约中都存在间隙数组 —— 在所有可能被继承的合约中包含 uint256[50] private __gap;
  • 间隙大小考虑了新变量 —— 添加变量时,相应地减小间隙大小:添加 2 个变量,将 uint256[50] 更改为 uint256[48]
  • 父合约间隙足够 —— 从可升级父合约继承的子合约需要间隙来存储它们自己的未来变量
  • 存储布局已记录 —— 维护文档,显示哪些槽被哪些变量使用

多级继承

  • 添加父变量是安全的 —— 向父合约添加变量可能会移动子存储槽。使用仅附加模式或专用存储结构
  • 用于复杂继承的钻石存储模式 —— 对于具有多重继承的合约,考虑使用钻石存储以避免槽冲突
  • 结构体打包是升级安全的 —— 更改结构体成员顺序或类型会破坏存储布局。仅附加新成员

危险信号

  • 可升级合约中没有存储间隙
  • 未遵循 ERC-1967 的手动存储槽分配
  • 在升级中将变量添加到存储布局的中间
  • 没有记录存储布局的复杂继承

2. 初始化和构造函数模式

为什么重要

代理合约不能使用构造函数,因为代理的存储与实现隔离。这种根本区别产生了独特的初始化漏洞,这些漏洞在常规合约中不存在。未受保护的初始化器已导致完整的协议接管。

安全检查

初始化器保护

  • 使用了初始化器修饰符 —— 所有初始化函数都使用 OpenZeppelin 的 initializer 修饰符或等效的保护
  • 强制执行一次性执行 —— 每个代理合约部署只能调用一次初始化器
  • 存在重入保护 —— 初始化器应具有重入保护,因为它们经常进行外部调用
  • 实施了抢跑保护 —— 使用访问控制或原子部署初始化模式来防止初始化抢跑

构造函数与初始化器分离

  • 在初始化器中进行关键设置,而不是在构造函数中 —— 必须在初始化器中进行所有者分配、收益模式或 gas 模式等配置
  • 构造函数仅设置不可变变量 —— 实现合约构造函数应仅设置 _disableInitializers() 不可变变量并调用
  • 构造函数中没有存储写入 —— 构造函数存储写入会影响实现合约,而不是代理合约存储

初始化状态管理

  • 所有关键变量已初始化 —— 显式设置所有者、管理员、费用接收者、精度值和操作参数
  • 零值验证 —— 阻止使用零地址或无效参数进行初始化
  • 初始化完整性检查 —— 验证在合约变为可操作状态之前,所有必需的初始化步骤均已完成
  • 父初始化器链接 —— 继承时,必须使用 onlyInitializing 修饰符调用所有父初始化器

实现合约安全

  • 在部署时初始化实现合约 —— 在实现合约构造函数中调用 _disableInitializers() 以防止直接初始化
  • 实现合约不能直接使用 —— 实现合约在没有代理合约上下文的情况下应该是不可用的

危险信号

  • 没有访问控制的公共初始化器
  • 实现合约构造函数中缺少 _disableInitializers()
  • 在构造函数而不是初始化器中进行关键配置
  • 没有针对初始化抢跑的保护

3. 升级机制和授权

为什么重要

不受限制的升级能力相当于让攻击者完全控制用户资金。薄弱的升级授权已导致无数次“rug pulls”,其中恶意实现耗尽协议资金。即使是合法的项目,当控制升级的单个密钥被泄露时也会受到影响。

安全检查

升级授权

  • 升级的多重签名要求 —— 升级需要来自不同方的多个签名(对于大型 TVL,最少 2/3,最好更高)
  • 关键升级的时间锁保护 —— 主要升级在执行前具有强制延迟期(最少 24-48 小时)
  • 基于角色的访问控制 —— 不同升级操作的不同角色,遵循最小权限原则
  • 升级提案透明度 —— 提议的实现在时间锁执行之前发布并可验证

实现合约验证

  • 接口兼容性检查 —— 新实现继承了必需的函数并保持兼容的接口
  • 存储布局兼容性 —— 自动工具验证存储布局兼容性(OpenZeppelin Upgrades 插件等)
  • 初始化兼容性 —— 新实现不需要可能重置关键状态的重新初始化
  • 代理兼容性验证 —— 对于 UUPS 代理,验证新实现是否包含正确的 proxiableUUID() 和升级函数

治理集成

  • 去中心化升级控制 —— 升级由治理代币/DAO 控制,而不是 EOA,这适用于项目成熟度
  • 紧急升级程序 —— 具有更高授权要求的清晰、有限的紧急升级能力
  • 升级执行监控 —— 对所有升级事务的链上监控和警报
  • 回滚能力 —— 在可能的情况下,如果发现问题,可以恢复到以前实现的机制

危险信号

  • 单个 EOA 控制生产系统的升级
  • 处理大量资金的升级没有时间锁
  • 执行升级不需要多重签名批准
  • 升级过程中缺少存储布局验证

4. Delegatecall 和执行上下文

为什么重要

Delegatecall 是使代理合约工作的机制,但它也是其最大的漏洞来源。当代理合约 delegatecall 实现合约时,实现合约代码在代理合约的存储上下文中运行。这种上下文切换会产生存储损坏、未经授权的访问和特定于实现的攻击的机会。

安全检查

合约存在性验证

  • 实现地址有代码 —— 在 delegatecall 之前检查 extcodesize > 0,以防止调用空地址
  • 实现地址验证 —— 验证实现地址是合约,而不是 EOA 或零地址
  • 实现一致性 —— 存储和验证实现哈希/codehash 以检测意外更改

上下文保留

  • 存储上下文感知 —— 实现函数必须了解它们在代理合约存储上下文中执行
  • msg.sender 保留 —— 验证 msg.sender 是否通过代理合约 -> 实现合约 -> 外部调用正确传播
  • msg.value 处理 —— 对于可支付函数,确保在 delegatecall 边界上正确处理 msg.value
  • 返回数据处理 —— 正确处理来自委派调用的返回数据大小和内容

Delegatecall 安全

  • 返回值验证 —— 检查 delegatecall 成功布尔值并验证返回的数据
  • Gas 处理 —— 为委派调用提供适当的 gas 转发和 gas 不足处理
  • Revert 原因传播 —— 将 revert 原因从实现合约转发到代理合约调用者以进行调试
  • 状态一致性 —— 即使委派调用失败,也要确保代理合约状态保持一致

危险信号

  • Delegatecall 到未检查的地址(用户输入、未验证的存储)
  • 在 delegatecall 之前没有合约存在性检查
  • 忽略 delegatecall 返回值
  • 委派调用的复杂 gas 计算

5. 访问控制和所有权

为什么重要

代理合约会创建额外的访问控制复杂性层。代理合约有自己的管理员/所有者角色,而实现合约可能有不同的角色假设。配置错误的访问控制可能会允许未经授权的升级、绕过预期的限制或创建权限提升漏洞。

安全检查

代理合约管理

  • 管理员角色分离 —— 代理合约管理员应与实现合约所有者/管理员角色不同
  • 管理员转移程序 —— 用于安全转移代理合约管理员权限的安全程序,最好使用接受/确认模式
  • 管理员密钥安全 —— 管理员密钥使用硬件钱包、多重签名或其他安全密钥管理
  • 管理员函数访问控制 —— 只有代理合约管理员可以调用升级函数,没有例外

实现合约访问控制

  • 上下文感知访问控制 —— 通过代理合约调用时,实现合约访问控制工作正常
  • 跨角色升级的一致性 —— 跨实现合约升级正确保留了管理员/所有者角色
  • 初始化访问控制 —— 只有授权方才能调用初始化器
  • 紧急函数保护 —— 紧急停止、暂停和恢复函数受到适当的限制

跨合约权限

  • 代理合约-实现合约权限同步 —— 授予代理合约地址的权限与实现合约逻辑一起使用
  • 外部协议集成 —— 其他协议正确识别代理合约地址以进行权限/允许
  • 签名验证兼容性 —— EIP-712 签名与代理合约上下文配合使用

危险信号

  • 同一地址控制代理合约管理员和实现合约管理员
  • 没有管理员角色转移程序
  • 实现合约访问控制依赖于 msg.sender,而不考虑代理合约上下文
  • 初始化函数缺少访问控制

6. 实现合约安全

为什么重要

由于所有代理合约共享同一个实现合约,因此实现合约中的漏洞会影响每个代理合约实例。受损或销毁的实现合约可以同时破坏数百或数千个代理合约实例,从而将其影响放大到远远超出单个合约故障的范围。

安全检查

自毁保护

  • 实现合约中没有 selfdestruct —— 实现合约永远不应包含 selfdestruct 功能
  • 限制了对外部合约的 Delegatecall —— 如果实现合约进行 delegatecall,请确保目标合约无法 selfdestruct
  • 库安全验证 —— 实现合约使用的外部库是安全的且无法销毁
  • 升级路径保留 —— 确保没有升级路径会导致具有 selfdestruct 的实现合约

实现合约不变性

  • 实现合约部署安全 —— 由受信任的部署者部署实现合约,而不是任意 EOA
  • 实现合约验证 —— 在区块浏览器上验证了实现合约源代码
  • 实现合约审计状态 —— 所有实现合约在部署前都要经过安全审计
  • 实现合约地址管理 —— 跟踪和监控代理合约实例使用的所有实现合约地址

库和外部依赖项安全

  • 库不变性 —— 外部库是以不可变方式部署的(它们本身不可升级)
  • 库审计状态 —— 实现合约使用的所有库都经过审计
  • 依赖项最小化 —— 尽量减少外部依赖项以减少攻击面
  • 版本锁定 —— 将库版本锁定到经过审计的发布版本,不要自动升级

危险信号

  • 实现合约包含 selfdestruct 功能
  • 实现合约对未知/用户控制的合约进行 delegatecall
  • 使用未经审计或可升级的库
  • 缺少实现合约地址验证

7. 函数选择器管理

为什么重要

代理合约根据函数选择器(函数签名的前 4 个字节)将函数调用转发给实现合约。当代理合约和实现合约具有具有相同选择器的函数时,代理合约函数优先,可能会绕过预期的访问控制或破坏预期的行为。

安全检查

避免选择器冲突

  • 没有 4 字节冲突 —— 验证代理合约管理员函数是否与实现合约函数具有相同的选择器
  • 冲突检测工具 —— 在开发过程中使用自动化工具检测潜在的选择器冲突
  • 系统的冲突分析 —— 对于复杂的代理合约,维护跨代理合约和实现合约的所有函数选择器的注册表
  • 透明代理模式合规性 —— 管理员只能调用管理员函数,用户只能调用实现合约函数

代理合约函数设计

  • 最小代理合约函数表面 —— 代理合约应公开最少的函数(升级、管理员更改等)
  • 清晰的函数分离 —— 代理合约管理函数和业务逻辑函数之间有明显的分离
  • 函数命名约定 —— 使用清楚地指示代理合约 vs 实现合约函数的命名
  • 选择器保留 —— 保留代理合约管理函数的函数选择器,以防止将来发生冲突

实现合约函数安全

  • 实现合约中没有类似于代理合约的函数 —— 除非专门设计为 UUPS,否则实现合约不应包含诸如 upgrade()changeAdmin() 之类的函数
  • 函数可见性适当 —— 实现合约函数具有正确的可见性(外部 vs 公共 vs 内部)
  • Delegatecall 感知 —— 实现合约函数考虑通过 delegatecall 调用

危险信号

  • 代理合约和实现合约之间存在重复的函数选择器
  • 增加冲突可能性的复杂代理合约接口
  • 实现合约包含没有明确目的的类似于代理合约管理函数
  • 在开发过程中没有系统的冲突检测

8. 常见代理合约漏洞

为什么重要

某些漏洞模式在不同的代理合约实现中反复出现。这些常见的陷阱是导致大多数与代理合约相关的漏洞利用的原因,应该在任何代理合约实现中进行系统地检查。

安全检查

初始化漏洞

  • 初始化抢跑保护 —— 部署和初始化应该是原子性的或访问控制的
  • 防止重新初始化 —— 每个代理合约实例只能调用一次初始化器
  • 部分初始化保护 —— 失败的初始化不会使代理合约处于不一致的状态
  • 参数验证 —— 验证所有初始化参数的正确性和安全性

升级漏洞

  • 存储布局保留 —— 新实现保持兼容的存储布局
  • 访问控制保留 —— 管理员/所有者角色正确转移到新实现
  • 状态迁移安全 —— 任何所需的状态迁移都是安全且经过测试的
  • 回滚程序 —— 存在回滚失败升级的程序

经济和 MEV 漏洞

  • 没有升级套利机会 —— 升级不会以牺牲用户为代价来创造 MEV 提取机会
  • 费用结构保留 —— 升级不会操纵费用计算以耗尽资金
  • Tokenomics 兼容性 —— 升级维护 tokenomics 不变量(供应、分配等)

集成漏洞

  • 外部协议兼容性 —— 升级不会破坏与其他协议的集成
  • Oracle/价格反馈兼容性 —— 升级后,价格反馈和 Oracle 继续正常工作
  • 跨链一致性 —— 对于多链部署,升级协调可防止不一致

危险信号

  • 没有对升级程序进行系统测试
  • 缺少对升级对外部集成的影响的验证
  • 未经用户同意修改核心 Tokenomics 的升级
  • 没有监控升级后的行为

UUPS 代理合约清单

对于实施通用可升级代理标准 (UUPS) 的团队:

实现合约要求

  • 存在 proxiableUUID() 函数 —— 实现合约必须包含正确的 UUID 以实现 UUPS 兼容性:0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
  • 在实现合约中授权升级 —— 升级逻辑驻留在实现合约中,而不是代理合约中
  • 升级函数的访问控制 —— 只有授权方才能调用升级函数
  • 实现合约地址验证 —— 在升级之前验证新的实现合约地址

升级函数安全

  • _authorizeUpgrade 保护 —— 使用适当的访问控制覆盖 _authorizeUpgrade
  • 升级原子性 —— 升级和任何必需的初始化以原子方式进行
  • 升级事件 emission —— 升级会发出用于监视和透明度的事件
  • Gas 优化 —— UUPS 升级比透明代理合约升级更节省 gas

存储管理

  • 实现合约存储保留 —— UUPS 实现可以具有存储变量(与透明代理合约实现不同)
  • 存储布局文档编制 —— 维护跨升级的存储布局的清晰文档
  • 间隙管理 —— 适当地使用存储间隙以进行将来的扩展

透明代理合约清单

对于使用 OpenZeppelin 的透明代理合约模式的团队:

管理员分离

  • 使用代理合约管理员合约 —— 使用 ProxyAdmin 合约,而不是直接管理员 EOA
  • 管理员无法调用实现合约 —— 透明代理合约管理员只能调用管理员函数,用户只能调用实现合约
  • 管理员函数保护 —— 管理员函数需要 ProxyAdmin 合约授权
  • 清晰的角色分离 —— 管理员操作和用户操作之间有明显的分离

实现合约限制

  • 实现合约中没有管理员功能 —— 实现合约不应包含代理合约管理员函数(可升级性由代理合约处理)
  • 实现合约是无状态的 —— 实现合约本身不存储状态,所有状态都在代理合约中
  • 实现合约初始化 —— 实现调用构造函数中的 _disableInitializers()

升级流程

  • ProxyAdmin 控制升级 —— 所有升级都通过 ProxyAdmin 合约进行
  • 实现合约验证 —— ProxyAdmin 在升级之前验证新的实现合约
  • 升级透明度 —— 所有升级都是透明的,并且可以通过 ProxyAdmin 进行审核

钻石标准清单

对于实施 EIP-2535 钻石标准的团队:

Facet 管理

  • 跟踪 Facet 函数签名 —— 钻石存储跟踪哪个 Facet 实现了每个函数选择器
  • Facet 之间没有选择器冲突 —— 函数选择器在所有 Facet 中都是唯一的
  • Facet 升级授权 —— 只有授权方可以添加/替换/删除 Facet
  • Facet 地址验证 —— 在安装之前,将新的 Facet 地址验证为合约

钻石存储

  • 使用钻石存储模式 —— 使用钻石存储结构体以避免 Facet 之间的存储冲突
  • 存储命名空间隔离 —— 每个 Facet 使用其自己的存储命名空间
  • 一致的存储访问 —— 所有 Facet 都一致地访问钻石存储
  • 存储布局文档编制 —— 为每个 Facet 记录存储布局

Cut 函数安全

  • diamondCut 访问控制 —— 只有授权方可以修改钻石 Facet
  • Cut 操作验证 —— 在执行之前验证所有添加/替换/删除操作
  • 原子 Facet 操作 —— Facet 修改是原子性的(全部成功或全部失败)
  • Cut 事件 emission —— 所有钻石修改都会发出适当的事件

回退函数

  • 正确的函数路由 —— 回退正确地将调用路由到适当的 Facet
  • 未知选择器处理 —— 正常处理对不存在函数的调用
  • Gas 优化 —— 高效的函数选择器查找和路由
  • 重入保护 —— 回退函数受到重入攻击的保护

测试你的代理合约实现

安全检查只有在你的测试良好时才有效。以下是你的测试套件应涵盖的内容:

存储布局测试

  • 跨升级存储一致性 —— 部署代理合约、升级实现合约并验证存储是否更改
  • 存储冲突检测 —— 自动测试存储槽冲突
  • 多重升级方案 —— 测试多个连续升级以进行存储布局保留
  • 继承测试 —— 验证复杂继承层次结构的存储布局

初始化测试

  • 一次性初始化 —— 验证初始化器只能调用一次
  • 抢跑抵抗 —— 在抢跑条件下测试初始化
  • 失败的初始化恢复 —— 测试初始化在部分完成时失败的行为
  • 参数验证 —— 测试初始化参数的所有极端情况

升级流程测试

  • 授权测试 —— 验证只有授权方可以升级
  • 实现合约验证 —— 使用无效实现合约测试升级过程
  • 存储迁移测试 —— 对于需要数据迁移的升级,测试迁移安全
  • 回滚测试 —— 测试回滚到以前实现的能力

访问控制测试

  • 角色分离测试 —— 验证代理合约管理员 vs 实现合约角色是否正常工作
  • 跨升级角色保留 —— 验证跨升级是否保留角色
  • 边缘情况授权 —— 测试授权边缘情况和边界条件
  • 多重签名集成 —— 测试与多重签名管理员合约的集成

安全方案测试

  • 恶意实现合约测试 —— 使用恶意实现合约测试代理合约行为
  • 选择器冲突测试 —— 测试发生选择器冲突时的行为
  • 重入测试 —— 测试所有函数的重入漏洞
  • Gas 消耗测试 —— 测试对 gas 消耗攻击的抵抗力

发布前:最终清单

  • [ ] 完成了多个独立的审计 —— 至少有两家信誉良好的公司进行了专门涵盖代理合约模式的审计
  • [ ] 考虑了形式验证 —— 对于高价值系统,请考虑对升级机制进行形式验证
  • [ ] 升级程序已记录 —— 用于执行升级的清晰、经过测试的程序
  • [ ] 应急响应计划 —— 制定响应代理合约漏洞的计划,包括升级和暂停程序
  • [ ] 监视和警报已在运行 —— 实时监视升级事件、管理员操作和异常行为
  • [ ] 启动了漏洞赏金计划 —— 激励安全研究人员查找特定于代理合约的漏洞
  • [ ] 计划了分阶段推广 —— 从有限的功能/资金开始,然后逐步增加
  • [ ] 存储布局工具 —— 将用于存储布局验证的自动工具集成到 CI/CD 中
  • [ ] 管理员密钥安全 —— 使用硬件钱包、多重签名或机构托管来保护代理合约管理员密钥
  • [ ] 用户沟通计划 —— 明确有关代理合约升级策略和用户保护的沟通

结论:构建持久的可升级合约

代理模式解锁了在部署后迭代和改进智能合约的能力,但它们引入了如果处理不当可能会造成灾难性后果的复杂性。安全代理合约实现与 1 亿美元的漏洞利用之间的区别通常归结为细微的细节:缺少存储间隙、未受保护的初始化器或函数选择器冲突。

使代理合约具有强大功能的升级能力也使其具有独特的危险性。与包含错误的常规智能合约不同,代理合约漏洞可能会影响依赖合约和用户的整个生态系统。

将此清单用作你的基础,而不是你的上限。每个代理合约实现都是唯一的,并且你的安全分析应考虑你的特定升级模式、访问控制模型和集成要求。

代理合约安全性出错的成本以数亿美元和破坏的用户信任来衡量。正确的成本是什么?有条不紊的开发实践、彻底的测试和全面的审计。

明智地选择。你用户的资金取决于此。

获得专家帮助

构建可升级合约或审计代理合约实现?代理合约安全性不仅复杂,而且是专门化的。

在 Zealynx,我们已经审计了 UUPS、透明、钻石和自定义模式中的数十个代理合约实现。我们知道隐藏在存储布局、初始化序列和升级机制中的细微漏洞。

准备好保护你的可升级合约了吗?获取报价直接联系 以讨论你的代理合约实现。

附加资源

常见问题解答:代理合约和可升级性安全

1. 代理合约漏洞的最常见原因是什么?

存储冲突和未受保护的初始化器占代理合约漏洞的 60% 以上。当代理合约和实现合约使用相同的存储槽时,会发生存储冲突,从而破坏数据。未受保护的初始化器允许任何人重新初始化合约并获得管理员访问权限。

2. 我应该使用 UUPS 还是透明代理合约模式?

UUPS 更节省 gas 且更灵活,但会将升级逻辑置于实现合约中(更复杂)。透明代理合约更简单、更安全,但会花费更多的 gas。对于新项目,如果你具有安全实施 UUPS 的专业知识,通常建议使用 UUPS。

3. 如何防止升级中的存储冲突?

遵循 ERC-1967 获取代理合约存储槽,在所有可升级合约中使用存储间隙(uint256[50] private __gap),记录存储布局,并使用 OpenZeppelin 的存储布局验证工具。永远不要将变量添加到现有存储布局的中间。

4. 我可以将代理合约升级到完全不同的合约吗?

从技术上讲,可以,但这非常危险。新的实现合约必须维护兼容的存储布局、函数接口和访问控制。添加新功能比完全替换现有逻辑更安全。

5. 如果我的实现合约被 self-destructed 会发生什么?

使用该实现合约的所有代理合约都将永久损坏。这就是为什么实现合约永远不应包含 selfdestruct 并且应被视为不可变基础设施的原因。始终在升级之前验证实现合约代码。

6. 代理合约实现应该获得多少次审计?

至少由具有代理合约模式经验的公司进行两次独立的审计。代理合约安全性是专门化的 —— 通用智能合约审计师可能会错过特定于代理合约的漏洞。对于高价值系统,请考虑进行其他审计。

词汇表

术语 定义
实现合约 包含实际业务逻辑的合约,由代理合约通过 delegatecall 调用
升级授权 限制谁可以升级代理合约实现合约的访问控制机制
函数选择器冲突 当代理合约和实现合约具有相同的 4 字节函数签名时,会导致不可预测的行为
实现合约初始化器 替换代理合约模式中构造函数的函数,用于设置初始合约状态
非结构化存储 使用 keccak256 哈希派生槽的存储模式,以防止代理合约和实现合约变量之间的冲突
  • 原文链接: zealynx.io/blogs/proxy-u...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
zealynx
zealynx
江湖只有他的大名,没有他的介绍。