Skip to content

guardian: executeRecovery 的链上 proposal 校验存在 TOCTOU 窗口(已知、暂不可闭合) #447

Description

@jhfnetboy

#441 复审时由两侧独立指出(R1b 与我自己的推导一致),Codex 判定利用门槛高。记录在案,说明为何暂不修,以及闭合它需要什么。

窗口

#441 给 executeRecovery 加的校验是:

读 activeRecovery()  ← check
   ↓  ← 窗口
writeContract executeRecovery()  ← use

两次 RPC 调用之间,链上 proposal 可以被覆盖。此时后端校验通过、但实际执行的是另一个 proposal —— 正是校验想防的场景。

为什么现在闭不上

合约的 executeRecovery() 无参数:

function executeRecovery() external;

没有任何办法把「我期望执行的是 newOwner=X 这个 proposal」表达进调用里。后端侧无论怎么写都消不掉这个窗口 —— 这是接口能力的缺失,不是实现疏忽。

为什么风险可接受(暂时)

  • 覆盖 proposal 需要已经是该账户的 guardian(proposeRecovery 有 guardian 门禁,proposeRecoveryWithSig 需要有效的 passkey guardian 签名)—— 攻击者本来就得先攻破 guardian 集合
  • 窗口是两次 RPC 往返,非常窄
  • 相比修复前(完全不校验)已是严格改进:随机的、非蓄意的分歧现在会被挡住

真正的闭合方式(需要合约侧)

给合约加一个带期望值的重载,例如:

function executeRecovery(address expectedNewOwner) external;  // 不匹配则 revert

或者让 executeRecovery() 接受 proposal nonce / hash。这样检查与执行就是原子的,后端只需把读到的值透传。

需要在 airaccount-contract 侧提 issue 并纳入接口版本管理(参照 Brood/orgs/aastar/INTERFACES.md 的跨仓库接口契约约定)。

关联

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions