隐私状态管理器(PSM)渗透测试评估

该文档是OpenZeppelin对Miden的隐私状态管理器(PSM)进行的灰盒渗透测试审计报告。报告详细描述了PSM的范围、系统概述、架构和安全模型,并识别出4个已解决的漏洞(1个高危、1个中危、2个低危)。报告认为,在实施推荐的修复措施后,PSM已达到生产就绪状态。

总结 时间线: 从 2026-02-04 → 到 2026-02-10 语言: Rust, TypeScript

发现

总问题数:4(4 个已解决)

严重:0(0 个已解决)· 高:1(1 个已解决)· 中:1(1 个已解决)· 低:2(2 个已解决)

备注和附加信息

0 条备注(0 条已解决)

范围

OpenZeppelin 对 OpenZeppelin 开发的用于 Miden (https://miden.xyz/) 的 PSM (private state manager) 进行了灰盒渗透测试。审计团队获得了应用程序的完整源代码访问权限,从而能够进行静态和动态分析。本次评估的范围主要集中在主要 API 端点及其底层基础设施。

渗透测试在 OpenZeppelin/private-state-manager 仓库的 upgrade-to-v0.13 分支上进行,提交版本为 7aa4461

范围内的文件如下:

 crates
├── server/*

更新: 报告中提到的所有解决方案和审计代码库的最终状态都包含在提交 eb89f8d

系统概述

Private State Manager (PSM) 是一个为 Miden private accounts 设计的同步和备份层。其主要目的是使设备或一组设备能够安全地协调状态更新,而无需信任其他参与者或服务器运营商。

Miden 架构

Miden 是一个注重隐私的零知识 (ZK) rollup,专为高吞吐量应用设计。与传统区块链中账户仅持有余额不同,Miden 采用 Actor 模型,其中每个账户都是一个智能合约,可以执行自定义逻辑、存储数据并自主管理资产。

该系统依赖于两个关键概念:

  • 账户:可编程实体,持有资产并执行代码。Miden 通过确保网络运营商在公共数据库中仅跟踪账户数据的加密承诺(哈希),而不是原始状态本身,从而提供强大的隐私保证。
  • Notes:类似于 UTXO 的消息,用于在账户之间交换数据和资产。与账户一样,Notes 也是可编程的,其详细信息保持私密,只有承诺在被消费之前存储在链上。

Miden 中的交易在客户端设备上本地执行,生成有效性的 ZK 证明。网络运营商验证此证明并更新全局状态数据库。尽管这种架构提供了卓越的隐私和可扩展性,但它将状态管理的负担转移给了用户。由于规范状态不是公开的,用户必须安全地管理自己的账户数据并在设备之间同步,PSM 旨在解决这一挑战。

Private State Manager (PSM)

PSM 是一项专门服务,旨在促进 Miden 账户链下状态的安全管理和同步。它充当一个隐私保护的协调层,允许用户(无论是从单个设备操作还是跨多个端点)维护其账户状态和交易历史的规范记录,而无需向公共网络暴露敏感数据或依赖中心化信任来验证。

PSM 的核心基于两个基本数据结构运行:

  • 状态:表示账户(包括资产、存储和 nonce)在特定时间点的快照。
  • Delta:捕获应用于该状态的不可变、只追加的更改。

该系统确保每个状态转换都与先前的承诺进行加密链接,防止历史重写或分叉。

账户生命周期

在与 PSM 交互之前,账户必须通过 /configure 端点提交其初始状态和身份验证策略来配置。PSM 会根据 Miden 网络验证这些信息并将其本地存储。为确保正确同步,系统管理的 Miden 账户应使用 PSM 客户端创建,该客户端将 PSM 的密钥整合到账户的身份验证组件中。

此设计要求有效交易携带 PSM 的签名,强制执行一种流程,即用户必须将交易提交给 PSM,允许其更新状态并签名,然后才广播到网络。此机制旨在防止直接与 Miden 网络交互以绕过 PSM,从而避免状态不同步。

Delta 提交和规范化

Delta 可以通过 /delta 直接提交,或通过多方账户的提案工作流进行协调。系统支持两种规范化模式:

  • 候选模式:提交的 deltas 以 candidate 状态存储。一个后台工作程序会定期根据链上状态验证候选 deltas,并将其提升为 canonical 或标记为 discarded
  • 乐观模式:Deltas 在提交后立即被标记为 canonical,并且状态是原子更新的。

规范化过程确保 PSM 的内部状态与 Miden 网络对账户的视图保持一致。

多方协调

对于由多个签名者控制的账户,PSM 提供了一个提案工作流:

  1. 提案人提交包含交易摘要的 delta 提案。
  2. 联署人检索待处理提案并附加其签名。
  3. 一旦达到所需阈值,任何授权方都可以通过 /delta 将提案提升为规范 delta。
  4. 规范化后,相应的提案将被删除。

这种机制使得私有多重签名协调成为可能,而无需向网络暴露内部状态。

架构

服务器提供了一个全面的 API,允许客户端注册账户、获取最新状态并提交新的 deltas。对于多方账户(如多重签名),PSM 包含一个 Delta 提案工作流。此功能支持不同签名者之间的协调,允许他们提出状态更改,收集其他参与者必要的签名,并仅在达到阈值后最终确定 delta。

与 PSM 的交互通过使用 Falcon 签名的自定义身份验证方案进行保护。请求必须包含 x-pubkeyx-signaturex-timestamp 头,确保只有授权方才能查询或修改特定账户的状态。

下面是本次评估范围内主要 API 端点的摘要:

方法 端点 描述
POST /configure 使用其初始状态定义在 PSM 上配置新账户。
POST /delta 为现有账户提交新的 delta(状态转换)。
GET /delta?account_id=<id>&nonce=<n> 通过账户 ID 和 nonce 检索特定的已确认 delta。
GET /head?account_id=<id> 获取账户的最新 delta(最高 nonce)。
GET /state?account_id=<id> 检索账户当前的规范状态。
GET /delta/since?account_id=<id>&nonce=<n> 检索自给定 nonce 以来应用的所有 deltas(用于同步)。
PUT /delta/proposal 为多方协调创建新的 delta 提案。
POST /delta/proposal/sign 向现有待处理提案添加联署人签名。
GET /delta/proposals?account_id=<id> 列出特定账户的所有待处理 delta 提案。

存储组件负责持久化账户快照、规范 deltas 和待处理提案。后端可插拔,支持文件系统或数据库实现(PostgreSQL)。

安全模型和信任假设

PSM 中的信任假设很少,因为它被设计为在没有复杂治理或权限模型的情况下运行。然而,执行环境的安全性至关重要。

PSM 的候选模式依赖于后台工作程序在将 deltas 提升为规范之前根据链上状态进行验证。然而,在乐观模式下,此验证被绕过:deltas 会立即被接受,而无需确认当前的链上承诺。如果区块链状态自 delta 的 prev_commitment 被捕获以来已经前进,PSM 的内部状态可能会悄悄地与链状态分歧。因此,假设此模式仅用于测试目的,并且不会在生产版本中启用。

还假定托管 PSM 的服务器基础设施是安全的,并遵循行业最佳实践以防止未经授权的数据访问或泄露。

PSM 支持两种类型的存储,使用文件系统或 Postgres。如与开发团队讨论的那样,文件系统只应用于测试环境。因此,假定对于生产环境,默认使用的存储将是 Postgres。

此外,建议 PSM 部署由 Web 应用程序防火墙 (WAF) 保护。这提供了针对常见网络威胁(如注入攻击和拒绝服务 (DoS) 尝试)的必要防御层,确保高可用性和弹性。

执行的测试用例

本次评估的测试方法基于既定的行业标准,包括 OWASP Web Security Testing Guide (WSTG)OWASP Application Security Verification Standard (ASVS)。还采用了针对 API 特定业务逻辑和架构定制的测试用例。

以下是为评估 PSM 安全性而进行的额外测试(超出上述方法论中概述的测试)列表:

结果 描述
⚠️ 可利用 尝试通过伪造 X-Forwarded-ForX-Real-IP 头来绕过速率限制
⚠️ 可利用 尝试在不使用 PSM 密钥的情况下在 PSM 上创建账户
⚠️ 可利用 文件系统模式下的路径遍历测试
安全 尝试在没有所有权或授权的情况下配置账户
安全 尝试仅使用其公共承诺配置账户
安全 验证直接与 Miden 网络交互的能力,绕过 PSM
安全 验证所有敏感端点是否严格执行身份验证
安全 评估头部认证机制的跨账户访问
安全 验证 x-pubkeyx-signature 头部强制执行
安全 账户信息端点(/state/delta 等)上的 IDOR 测试
安全 在发送提案、签名和查询中使用畸形 payload 以使 PSM 崩溃
安全 使用不同的联署人来操纵账户状态
安全 注入漏洞分析,主要是在使用 Postgres 时的 SQL 注入

高危

通过路径遍历进行任意提案签名

sign_delta_proposal 函数接受用户提供的 commitment 参数,并在文件路径构造中直接使用它,而没有进行充分的验证。具体来说,尽管 account_id 参数被严格验证为十六进制编码的字段元素,但 commitment 参数仅检查是否存在“0x”前缀,如果存在则会删除。

这允许经过身份验证的攻击者提供包含目录遍历序列(例如,../)的 commitment 值。当 FilesystemService 使用此值构造提案的文件路径时,它可能会解析到攻击者账户预期目录之外的文件。该漏洞存在于 get_delta_proposal_path 方法中,该方法将用户提供的 commitment 与基本存储路径连接起来。

攻击者可以读取和修改属于其他账户的 delta 提案。这违反了系统中租户之间预期的隔离。通过修改另一个账户的提案文件,攻击者可以向其中附加自己的签名。虽然这可能不会直接损害受害者的私钥,但它会破坏受害者数据状态,并可能导致拒绝服务或提案过程中的混乱。

以下场景演示了攻击者(账户 A)如何修改属于受害者(账户 B)的提案。

  1. 攻击者以 账户 A 身份进行身份验证。

  2. 受害者(账户 B)有一个带有承诺哈希 TARGET_HASH 的待处理提案。文件存储在 .../storage/ACCOUNT_B/proposals/TARGET_HASH.json

  3. 攻击者使用以下参数调用 PUT /delta/proposal 端点:

    • account_idACCOUNT_A
    • commitment../../ACCOUNT_B/proposals/TARGET_HASH
    • credentials:账户 A 的有效签名。

    请注意,攻击者必须省略 payload 中目标哈希的 0x 前缀。服务器的逻辑只有当整个字符串以 0x 开头时才会删除它。由于 payload 以 ../ 开头,因此绕过了前缀删除,并直接附加了 .json

  4. 服务器构造路径:.../storage/ACCOUNT_A/proposals/../../ACCOUNT_B/proposals/TARGET_HASH.json

  5. 文件系统将此路径解析为受害者的提案文件。服务器读取文件,附加攻击者的签名,并将其写回,成功修改了受害者的数据。

请考虑验证 commitment 参数,以确保它是一个表示哈希的有效十六进制字符串,然后才能在文件路径操作中使用它。或者,考虑使用安全的路径连接机制,防止遍历到预期目录之外。

更新: 已在 pull request #133 中解决。

中危

账户注册时缺少 PSM 承诺验证

PSM 未能通过验证所注册的账户是否真正将 PSM 列为其授权的联署人来最大化安全性。在处理 configure 请求时,服务器使用 validate_credential 方法正确地根据账户的公钥(在 Slot 0 或 1 中)验证用户的签名。

然而,它没有检查 Slot 5 的内容(其中存储了 psm_commitment)。这允许攻击者创建一个有效的 Miden 账户,其中 Slot 5 包含由攻击者自己控制的公钥,而不是 PSM 的密钥,并且仍然成功地将此账户注册到 PSM 服务。

攻击者可以利用 PSM 作为状态跟踪和数据可用性服务,而不受其安全策略的约束。由于攻击者持有用户槽位和“伪”PSM 槽位(Slot 5)的私钥,他们可以完全离线构建和签署交易,从而绕过 PSM 运营商预期的任何联署逻辑。PSM 变成了一个无法控制账户的不知情的观察者。

请考虑在 configure_account 中添加一个验证步骤,明确读取所提供的 initial_state 的 Slot 5 中的承诺。服务器应将此承诺与其自身公钥的承诺进行比较,如果不匹配则拒绝配置请求。

更新: 已在 pull request #134 中解决。

低危

通过代理头伪造绕过速率限制

extract_client_ip 中的速率限制中间件信任 X-Forwarded-ForX-Real-IP 头,而没有验证请求是否来自受信任的代理。

当服务器直接暴露在互联网上(绕过任何反向代理)时,攻击者可以在每个请求上使用不同的伪造 IP 地址来伪造这些头,从而有效地完全绕过基于 IP 的速率限制。在每 IP 每秒 10 个请求的默认突发限制下,攻击者可以通过改变伪造的头来实现明显更高的吞吐量。

严重性取决于部署配置。如果服务器始终部署在反向代理之后,并且该反向代理会剥离或覆盖客户端提供的代理头,则此漏洞不可利用。

考虑实现受信任的代理配置,仅当直接连接 IP 与配置的受信任代理地址列表匹配时才信任 X-Forwarded-ForX-Real-IP 头。或者,考虑文档说明在经头净化反向代理或 WAF (Web Application Firewall) 之后部署是安全要求。

更新: 已在 pull request #136 中解决。团队表示:

我们对生产部署的计划是实际在服务器中设置一个代理,该代理将使用 PSM_TRUSTED_PROXY_IPS 环境变量列入白名单,这样 X-Forwarded-ForX-Real-IP 就可以被信任。

通过 Delta 提案耗尽内存

push_delta_proposal 函数允许用户提交 delta 提案,而不限制待处理提案的数量。每个提案都作为单独的文件存储在服务器上。get_delta_proposals 函数随后会同时将账户的所有提案 读取 到内存中。攻击者可以利用这一点,通过提交大量提案,导致过度的内存消耗,从而使服务器进程崩溃。

push_delta_proposal 函数仅检查待处理的候选 deltas,但并未限制 proposals 端点中待处理提案的数量。这使得攻击者只要提供唯一的交易摘要,就可以持续提交新提案。

此漏洞可用于对 Private State Manager 执行 DoS 攻击。通过耗尽服务器内存,攻击者可以使服务器崩溃或阻止合法用户与服务交互。对于经过身份验证的用户而言,攻击成本相对较低。

考虑对每个账户允许的待处理提案数量实施限制。达到此限制时,服务器应拒绝新的提案提交,直到现有提案被处理或丢弃。此外,考虑在 GET /delta/proposals 请求中实现分页。

更新: 已在 pull request #135 中解决。

结论

本次评估共发现了 4 个漏洞,包括 1 个高危、1 个中危和 2 个低危问题。PSM 服务器提供了一个简洁且结构良好的 API,有效实现了身份验证机制,从而减轻了与多账户泄露相关的主要风险。

建议的修复足以使 PSM 达到生产就绪状态。鉴于发现的性质和修复路径的清晰性,在修复审查后无需进行重新审计。

OpenZeppelin 开发团队在整个合作过程中表现出卓越的协作精神,值得称赞。他们高度响应并积极参与了关于 PSM 架构、潜在风险及其与 Miden 网络集成的广泛讨论,这极大地促进了评估过程。

附录

问题分类

OpenZeppelin 将智能合约漏洞分为 5 个级别:

  • 严重
  • 高危
  • 中危
  • 低危
  • 备注/信息

严重

当问题的影响是灾难性的,威胁到客户声誉的巨大损害和/或对客户或用户造成严重的经济损失时,适用此分类。利用的可能性可能很高,需要迅速响应。严重问题通常涉及重大风险,例如大量用户敏感资产的永久丢失或锁定,或核心系统功能的失败而没有可行的缓解措施。这些问题因其可能严重损害系统完整性或用户信任而需要立即关注。

高危

这些问题的特点是可能对客户的声誉造成重大影响和/或导致可观的经济损失。利用的可能性很高,需要迅速响应。此类问题可能包括大量用户敏感资产的临时丢失或锁定,或关键系统功能的破坏,尽管有潜在但有限的缓解措施可用。重点在于对系统运行或资产安全的重大而非总是灾难性的影响,需要及时有效的补救。

中危

被归类为中危的问题可能对客户的声誉产生明显的负面影响和/或造成中等程度的经济损失。如果置之不理,此类问题具有中等程度的被利用可能性,或可能在系统中引起不必要的副作用。这些问题通常仅限于用户敏感资产的一小部分,或可能涉及与指定系统设计的偏差,虽然不直接涉及财务,但会损害系统完整性或用户体验。这里的重点是那些构成真实但受控风险的问题,需要及时关注以防止升级。

低危

低危问题是指对客户运营和/或声誉影响较小的问题。这些问题可能代表对客户特定业务模型的轻微风险或低效率。它们被确定为改进领域,虽然不紧急,但如果得到解决,可以提高代码库的安全性和质量。

备注和附加信息

此类别适用于影响极小但仍需解决的问题。解决这些问题有助于提升整体安全态势和代码质量,但不需要立即采取行动。它反映了即使在不构成直接风险的领域,也致力于保持高标准和持续改进。

准备好保护你的代码了吗?

请求审计 →

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

0 条评论

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