Uniswap v4 安全审计:DeFi 顶尖专家的关键安全教训

  • Certora
  • 发布于 2025-01-21 14:56
  • 阅读 20

Uniswap v4 引入了新特性,但随之而来的复杂性引发了一系列的安全审计问题。本文详细讨论了三项重大漏洞,包括资金双重计数、价格不变条件违反和手续费收集的干扰,强调了在复杂 DeFi 系统中确保安全性的必要性。

Uniswap v4 是去中心化交易所的最新进化,引入了变革性功能,如 hooks 和统一的池结构,以重新定义自动化市场制造商 (AMMs)。随着创新而来的复杂性,需要进行严格的安全审计。

为了加强其设计,Uniswap Labs 聘请了四家顶尖安全公司:OpenZeppelinSpearbitTrail of BitsCertora。这些团队发现的漏洞包括从可能危及用户资金的严重风险到可能降低性能的轻微低效问题。

在本文中,我们重点介绍 三个突出的发现,评估它们的影响,并总结为保障先进 DeFi 系统安全所需的重要教训。

审计概览

下表总结了参与审计的团队、评估的时间线以及识别的问题数量。我们鼓励读者查看链接的报告以获取详细的发现。

OpenZeppelin

  • 发现的漏洞:7
  • 开始:2024年7月

Certora

  • 发现的漏洞:5
  • 开始:2024年7月

Spearbit

  • 发现的漏洞:6
  • 开始:2024年9月

Trail of Bits

  • 发现的漏洞:1
  • 开始:2024年9月

分析 Uniswap v4 中最关键的漏洞

让我们看看在上述审计中发现的三个主要漏洞,弄清楚它们发生的原因,并学习如何使协议更加可靠。

1. CELO 上的存款双重计数

OpenZeppelin 在 CELO 区块链上发现了一个 关键漏洞,这可能允许攻击者利用 Uniswap 的记账机制。这个漏洞被认为是必要的,因为它允许攻击者提取比他们存入的更多的资金,从而有效地使协议遭受盗窃。

这样的缺陷威胁到平台的财务稳定性,如果大规模利用,可能导致流动性完全耗尽。这个漏洞源于 CELO 的原生代币与其 ERC-20 对应代币交互的独特方式,允许重复提款,并可能耗尽协议。

理解这个漏洞

Uniswap 的记账通过跟踪 余额增量 来运作。它包括三个关键功能:

  1. sync: 记录当前代币余额。
  2. settle: 计算余额增量(代币余额的变化)并记入用户。
  3. take: 允许用户提取其记入的代币。

在 CELO 上,协议错误地将原生代币和其 ERC-20 表现的存款都记入,从而为攻击者创造了一个漏洞。

漏洞利用场景

  1. 与 ERC-20 代币同步: 攻击者调用 sync 函数,使用 CELO 的 ERC-20 代币来记录余额。
  2. 与原生代币同步: 攻击者调用 sync 函数,使用原生 CELO 代币来记录余额。
  3. 转账: 将资金从攻击者转移到协议中的 CELO 原生代币,二者等同于 ERC20。
  4. 与原生代币结算: 攻击者存入 CELO 的原生代币(数量为 X),这使得用户的帐户信用记入 X 个代币。
  5. 与 ERC-20 代币结算: 在没有任何额外转账的情况下,攻击者再次调用 settle 函数,使用 ERC-20 代币。系统错误地再记入了 X 个代币。
  6. 通过 take 进行双重提款
  • 调用 take 函数,使用 CELO ERC-20 代币提取 X 个代币,使 ERC-20 代币的增量变为零。
  • 调用 take 函数,使用原生 CELO 代币提取另一个 X 个代币,使原生代币的增量变为零。

影响

攻击者在仅存入 X 的情况下提取了 2X 个代币,并重复该过程以耗尽 CELO 的 ERC20 代币池。

为什么会发生这种情况

这个漏洞源于 Uniswap 无法区分 CELO 的原生代币和其 ERC-20 表现。这一边缘情况特定于 CELO,而在处理标准原生代币的区块链上是不可行的。

结论

开发者必须考虑涉及非标准代币和区块链特性的边缘情况,特别是在设计多链协议时。

具体针对这种情况,保持用户流程尽可能简单可以防止意外后果。在这个漏洞的案例中,用户不应在没有及时结算的情况下再次同步,这是该漏洞的缓解措施。

2. Tick 价格不变性违反

Certora 在 Uniswap v4 中发现了一个 中等严重性的漏洞使用形式验证 工具。该方法利用数学证明来验证协议逻辑在所有场景下是否按预期行为。

通过严格分析代码,形式验证特别有效于发现传统测试方法可能遗漏的微妙漏洞。形式验证是一种证明协议逻辑正确性的数学方法,使其在发现微妙漏洞方面十分有效。

理解这个漏洞

该问题出现在大量联系 tick 价格和池状态变量的一项重要不变条件遭到违反时,导致可能的资金错误配置。

在 Uniswap v4 中,每个池保持两个关键变量:

  1. pool.tick:表示池的当前 tick。
  2. pool.sqrtPrice:表示池当前价格的平方根。

该协议确保这些变量遵循以下不变条件:

tickAtSqrtRatio(pool.sqrtPrice) == pool.tick

该不变条件确保池的 tick 和价格之间的一致性,使系统在交换期间维持准确的计算。然而,在我们使用 Certora Prover 对这一不变条件进行形式验证的过程中,我们发现了一种违反该保证的场景。

漏洞利用场景

  1. 攻击者发起一个交换,耗尽当前 tick 的流动性并穿越到下一个 tick。
  2. 协议将 pool.tick 更新到下一个 tick,但未能正确调整 pool.sqrtPrice
  3. 这导致 pool.sqrtPrice 与更新的 pool.tick 不再对齐。

影响:

  • 资金错配: 不正确的价格读取可能导致捐赠被发送到错误的 tick,造成资产配置错误。
  • 外部误解: 查询池状态的观察者会看到不对齐的 tick 和价格,这可能导致对池的健康状况或行为的不正确假设。

为什么会发生这种情况

问题源于在耗尽 tick 这样的边缘情况下,准确更新多个状态变量的复杂性。尽管 Uniswap 团队承认我们的不变量是正确的,但其违反揭示了实现中的微妙失误。

结论

在定义不变条件时,开发者必须利用协议的设计原则,揭示隐藏的边缘案例,并确保系统行为与预期一致。

3. 交换时收集协议费用

Trail of Bits 识别了一个 低严重性的问题,在交换期间调用 collectFees 函数会干扰余额跟踪,从而可能减少用户的回报或导致交易失败。

理解这个漏洞

Uniswap v4 允许用户在交易期间调用各种函数,包括收集协议费用的函数。然而,当在 актив“交换”过程中调用 collectFees 时,余额增量在交易进行中暂时被修改,反映未结算的金额。这一冲突导致计算错误。

漏洞利用场景

  1. 用户发起一个 交换,导致池的余额增量临时更新以反映正在进行的交易。
  2. 在交换期间,用户调用 collectFees 函数以获取他们的部分已收取费用。
  3. collectFees 函数更新了余额增量,而在交换过程中,余额增量暂时为负数。这实际上将用户应收的费用抵消在他们的未清余额上。
  4. 当随后的 settle 函数被调用以完成交换时,用户的调整余额增量导致:
  • 减少的回报: 用户获得的数量少于预期。
  • 交易回退: 如果增量保持不正确,交易则会失败。

影响

虽然财务影响较小,但此漏洞通过以下方式降低了用户体验:

  • 减少回报。
  • 强迫用户重试交易。

为什么会发生这种情况

余额增量在交换期间临时反映未结算的值。可以将其视为显示正在进行但尚未完成交易的账本。例如,如果你在平衡支票帐户,未结算的值代表你已经写的但尚未结清的支票。在此阶段调用 collectFees 会干扰系统的中间状态。

结论

为防止此类问题,用户应避免在交换期间调用如 collectFees 这样的函数,或协议应明确阻止此类调用。确保正确的操作顺序对于避免冲突并维护系统的完整性至关重要。

结论

本文中提出的发现强调了在开发安全和稳健的 DeFi 协议时的重要教训。尽管作为最经过测试和成熟的协议之一,但在多次 审计和形式验证 中发现的漏洞演示了处理边缘案例、通过第一原则推理创建鲁棒不变量和确保复杂系统中准确功能执行的重要性。

Uniswap 聘请各种独立安全团队的决定突显了多样化审计视角在识别漏洞中的必要性。这种协作方法加强了协议,同时确保了在多样区块链环境中的安全性和无缝的用户体验。

  • 原文链接: certora.com/blog/uniswap...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Certora
Certora
Securing DeFi through smart contract audits, formal verification, and protocol design reviews.