首页>>资讯>>产业

ERC4337 和智能钱包的安全注意事项

2024-09-20 10:34:18 57

账户抽象


账户抽象是一个广泛的话题,但在非常高的层面上,其理念是将账户的概念抽象为一个智能合约(即 智能钱包),这比大多数人今天在与区块链交互时使用的外部拥有账户(EOA)提供了更多的灵活性。其一些好处包括:


提高安全性:在丢失访问权限的情况下实施社交恢复。授权密钥可以在不需要移动资产的情况下进行轮换。

赞助交易:用户不需要拥有ETH,第三方实体(称为赞助交易方)可以赞助交易费用。

替代签名方法:智能钱包可以指定任何签名协议。

Gas效率:可以在单个交易中批量处理多个操作以提高gas成本。

一旦与EOA的限制脱钩,账户的灵活性仅受限于智能合约中可以编程的内容。


标准


以太坊中实现账户抽象的提议标准定义在EIP-4337中。

24.png

这可能是目前最好的描述ERC-4337的图表,感谢The Red Guild。


操作(Action)

操作由称为用户操作的对象表示。

24.png

可以在这里找到每个详细解释,但我们可以看到一些熟悉的名称,它们也是以太坊交易的一部分:sender,nonce,callData,gasFees(打包了maxFeePerGas和maxPriorityFeePerGas)。签名属性是一个任意的有效载荷,将由钱包实现使用。


捆绑器 (Bundler)


捆绑器运行一个服务器,在备用内存池中收集用户操作。他们负责执行这些操作,最终将其打包成一批用户操作并发送到Entrypoint。


捆绑器支付gas,但期望他们的成本得到补偿,并收取服务费。


Entrypoint


Entrypoint 是链上中心角色。它是一个单例智能合约,受到其他各方的信任,将处理捆绑器、账户和赞助交易方之间的交互,以协调操作的验证和执行。


工厂和账户

工厂负责创建实际的账户。如果账户尚未部署,用户操作可以包含一个initCode,将通过工厂用于初始化合约。


合约创建利用CREATE2操作码提供确定性地址。这有助于模拟EOA的行为:无需事先部署代码即可安全地预计算发送者地址。


账户合约通常会在第一次交互期间创建。


赞助交易方(Paymaster)


标准的一个关键特性是能够赞助交易。赞助交易方是生态系统中定义的另一个实体,可以提供所需资金以支付操作的成本。这可以极大地帮助新用户入驻并改善整体体验,并且一直是账户抽象的主导用例之一。


例如,特定协议可能会赞助其合约的交易以激励交互。另一个有用的场景是赞助交易方从账户中提取ERC20代币作为支付,启用无gas交易。


安全注意事项


正如提案所述,主要挑战是防止拒绝服务(DoS)攻击。虽然某些链上交互很容易验证(例如检查签名是否有效),但确保愿意执行(不受信任的)操作的构建者得到补偿(否则不执行)。如果攻击者包含故意回滚的操作会发生什么?我们可以添加验证,但谁来支付验证所花费的gas成本?如果是捆绑器故意给用户带来麻烦呢?操作可以在链下模拟,但如何确保这些操作在链上运行时具有相同的结果?


此外,想象一下如果捆绑器直接与账户交互会发生什么。捆绑器不能信任账户会偿还费用,账户也不能信任捆绑器不会发送无法执行但会消耗gas(并且必须支付)的无效操作。


解决这个问题的方法是将验证与执行分开。这种方法允许我们在验证阶段应用严格的约束,而不干扰操作本身的执行。两个主要限制是:


禁止某些可以从环境中检索信息的操作码(例如TIMESTAMP,NUMBER或GASPRICE,完整列表见EIP-7562部分操作码规则)。

限制对存储的访问,以防止未来的变更干扰操作的结果。


关键在于使验证步骤尽可能纯净,希望其链下模拟可以准确预测链上会发生什么。捆绑器只需关心操作的验证。任何无效操作支付的gas将归于捆绑器,但失败的操作(经授权后)由发送者支付。这样,捆绑器可以在验证步骤上运行模拟,提高其链上执行成功的信心。


验证与执行的分离是拥有中心 Entrypoint 的主要原因。我们可以在验证步骤中施加限制,同时在稍后允许任意执行。通过Entrypoint运行操作为捆绑器提供了更好的保证,并允许账户安全地将验证与执行分离(记住Entrypoint是一个受信任的实体)。


简而言之,Entrypoint执行以下操作:


验证步骤。对于每个操作:

验证账户有足够的资金支付最大gas量

验证账户中的操作(validateUserOp())

执行步骤。对于每个操作:

执行操作并跟踪gas成本

将费用返还给捆绑器


我们可以在参考实现中清楚地看到这种模式:

24.png

注意这里的循环如何工作:验证全部在一个单独的循环中完成。我们不希望一个操作的执行干扰另一个操作的验证。


拥有一个中心Entrypoint合约来协调这个过程,使得不同的参与者能够验证其他人是否行为正确。在执行时,账户只需检查调用者是否为Entrypoint,因为它可以信任Entrypoint已经验证了操作。如果没有这个受信任的实体,验证与执行的分离是不可能的。

24.png

账户执行仅检查调用者是否为Entrypoint,因为它可以信任操作已被先前验证。示例来自SimpleAccount.sol。


当涉及到paymaster时,同样的冲突也会出现,我们需要调用validatePaymasterOp()来检查paymaster是否愿意赞助该操作。然而,这里的情况有些不同。单个paymaster可能会处理来自不同发送者的多个用户操作,这意味着一个操作的验证可能会干扰另一个操作,因为paymaster的存储在所有具有相同paymaster的操作包中是共享的。在此函数中限制存储访问将非常有限,这将严重减少paymaster的能力(参见EIP-7562部分Unstaked Paymasters Reputation Rules)。


恶意的paymaster可以导致拒绝服务。为了减轻这种攻击向量,标准提出了一个质押和声誉系统。paymaster需要质押ETH。捆绑器也会跟踪失败的验证,并可以限制或直接禁止不合作的paymaster。请注意,质押永远不会被削减。质押的目的是为了减轻女巫攻击,以便paymaster不能简单地转移到一个新的账户并拥有新的声誉。


一个重要的细节是,paymaster也被允许在主要操作完成后通过调用postOp()来执行。在验证阶段,paymaster可以检查在操作执行之前是否满足某些条件,但在执行过程中这些条件可能很容易失效。例如,一个拉取ERC20代币来支付费用的paymaster可以验证发送者是否有足够的代币(以及足够的授权),但操作的执行可能有意或无意地改变这一点。对postOp()的失败调用可能会使操作回滚,但此时gas已经被消耗,这将由paymaster承担,从而使恶意账户能够进行悲伤攻击(griefing attacks)。


这个问题的解决方案非常有趣,因为它非常简单,我们调用paymaster的postOp()两次。第一次调用发生在与操作的主要执行一起的内部上下文中。如果第一次调用回滚,那么操作也会回滚,并触发第二次调用,此时操作的效果被取消。

24.png

工厂不仅能够实现确定性部署,还为各种参与者提供了更强的保证。与处理浅层字节码字符串不同,具体且已知的工厂的存在允许更好的可见性和分析,同时提供额外的安全性。例如,paymaster可以通过简单地检查目标工厂来决定是否赞助钱包创建。对于捆绑器来说,通过拥有一个确保没有链上回滚的知名实现,模拟的复杂性大大降低。此外,它为用户提供了更好的安全性,因为工厂合约地址比任意初始化代码更容易分析,从而实现更好的工具和用户体验。

24.png

账户在验证阶段使用工厂创建。摘录自SenderCreator.sol。


由于钱包部署本质上与所需的授权账户分离,因此将钱包初始化与其地址链接非常重要,正如标准所指出的。否则,攻击者最终可能会使用他们的凭证部署一个钱包。这通常通过将签名与创建参数(可能是salt或init代码哈希)相关联来实现。因此,更改授权账户将导致不同的地址。


由于账户创建也是验证阶段的一部分(我们需要在验证账户上的操作之前将其部署),工厂与paymaster具有相同的条件。它们必须要么质押,要么将其存储空间限制在钱包的域内。


已知的实现问题


销毁钱包实现


工厂通常使用克隆模式来部署新钱包。它们有一个实现实例,并创建指向该实现的代理。如果任何人可以接管实现实例并销毁它,那将使所有代理无法使用,导致所有钱包失效。这已通过自毁的弃用得到缓解,但在其他链上仍然可以被利用。


EIP4337Manager自毁问题

SmartAccount实现的销毁

AmbireAccount实现可以通过权限销毁


Gas


Gas在系统中起着至关重要的作用,它是确保操作成功执行和适当补偿的关键。由于其使用的众多规则,正确的gas跟踪是一项困难的任务,这可能导致许多潜在的陷阱。


攻击者故意增加数据大小以增加费用。


在转发交易时盗取资金


恶意捆绑器通过提供不足的gas来阻碍操作执行。即使用户操作指定了gas限制,如果运行上下文没有足够的gas,它仍将使用可用的量执行调用。


由于恶意捆绑器提交的gas值不足导致的用户操作拒绝服务和用户交易费用损失


不正确的钱包初始化


如前所述,将钱包地址与其所有权相关联非常重要,否则任何人都可以部署并恶意初始化它。


攻击者可以控制反事实钱包


签名


签名问题值得专门撰写一篇文章,但许多常见问题也可以在账户抽象方案中看到。


钱包无法验证合约签名


由于签名验证不足,可能进行任意交易

可以使用合约签名绕过SmartAccount授权


Paymaster签名可以重放


由于签名验证不足,可能进行任意交易

ERC4337示例VerifyingPaymaster签名重放攻击


参数可变性由于不属于签名数据的一部分


FeeRefund结构中的tokenGasPriceFactor在调用execTransaction时可能是可变的

模块启用模式流程中的模块类型参数是可变的


由于无效的nonce检查导致的签名重放攻击


重放攻击(EIP712签名交易)

缺少nonce在_getEnableModeDataHash()中允许签名重放


跨链重放


跨链签名重放攻击

由于在所有执行路径中未增加nonce导致的交易重放


取消后的恢复交易可以重放

使用相同所有者的不同账户进行ERC1271重放。文章包括关于谁对验证负责的良好讨论。


ERC1271重放

不正确的EIP-712签名

ModuleEnableMode结构的Typehash不正确### Griefing


最轻微的不正确假设可能会为系统中的某个行为者带来负面影响的窗口。


在以下问题中,攻击者可以抢先调用 handleOps() 来执行至少一个捆绑操作,导致原始批处理回滚。


对 handleOps 和 multiSend 逻辑的 Griefing 攻击


通过滥用 EIP-150,可以强制可选调用失败。在下一个问题中,执行者可以故意提供较少的gas,以便回滚内部调用,同时在外部上下文中仍有足够的gas来完成交易。


攻击者可以强制使用 tryCatch 的交易失败


未能遵守标准


该标准相当复杂且实现起来并非易事。遵守所有细微差别可能是一项艰巨的任务。


不符合 EIP-4337

协议未完全符合 EIP-7579


错误验证


在这个主题中,不正确的验证并不少见。在这个问题中,Entrypoint 不允许在钱包上执行操作,完全破坏了账户抽象集成。


Entrypoint 使用的方法有 onlyOwner 修饰符

由于缺少授权控制,任何人都可以调用 fallbackFunction


参考


https://eips.ethereum.org/EIPS/eip-4337

https://eips.ethereum.org/EIPS/eip-7562

https://github.com/eth-infinitism/account-abstraction

https://www.alchemy.com/blog/account-abstraction

https://www.youtube.com/watch?v=f-W6O0tIm2Y (spanish content)

https://code4rena.com/reports/2023-01-biconomy

https://code4rena.com/reports/2023-05-ambire

https://codehawks.cyfrin.io/c/2024-07-biconomy/results个主要限制是:

声明:本网站所有相关资料如有侵权请联系站长删除,资料仅供用户学习及研究之用,不构成任何投资建议!