该文档是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 是一个注重隐私的零知识 (ZK) rollup,专为高吞吐量应用设计。与传统区块链中账户仅持有余额不同,Miden 采用 Actor 模型,其中每个账户都是一个智能合约,可以执行自定义逻辑、存储数据并自主管理资产。
该系统依赖于两个关键概念:
Miden 中的交易在客户端设备上本地执行,生成有效性的 ZK 证明。网络运营商验证此证明并更新全局状态数据库。尽管这种架构提供了卓越的隐私和可扩展性,但它将状态管理的负担转移给了用户。由于规范状态不是公开的,用户必须安全地管理自己的账户数据并在设备之间同步,PSM 旨在解决这一挑战。
PSM 是一项专门服务,旨在促进 Miden 账户链下状态的安全管理和同步。它充当一个隐私保护的协调层,允许用户(无论是从单个设备操作还是跨多个端点)维护其账户状态和交易历史的规范记录,而无需向公共网络暴露敏感数据或依赖中心化信任来验证。
PSM 的核心基于两个基本数据结构运行:
该系统确保每个状态转换都与先前的承诺进行加密链接,防止历史重写或分叉。
在与 PSM 交互之前,账户必须通过 /configure 端点提交其初始状态和身份验证策略来配置。PSM 会根据 Miden 网络验证这些信息并将其本地存储。为确保正确同步,系统管理的 Miden 账户应使用 PSM 客户端创建,该客户端将 PSM 的密钥整合到账户的身份验证组件中。
此设计要求有效交易携带 PSM 的签名,强制执行一种流程,即用户必须将交易提交给 PSM,允许其更新状态并签名,然后才广播到网络。此机制旨在防止直接与 Miden 网络交互以绕过 PSM,从而避免状态不同步。
Delta 可以通过 /delta 直接提交,或通过多方账户的提案工作流进行协调。系统支持两种规范化模式:
candidate 状态存储。一个后台工作程序会定期根据链上状态验证候选 deltas,并将其提升为 canonical 或标记为 discarded。canonical,并且状态是原子更新的。规范化过程确保 PSM 的内部状态与 Miden 网络对账户的视图保持一致。
对于由多个签名者控制的账户,PSM 提供了一个提案工作流:
/delta 将提案提升为规范 delta。这种机制使得私有多重签名协调成为可能,而无需向网络暴露内部状态。
服务器提供了一个全面的 API,允许客户端注册账户、获取最新状态并提交新的 deltas。对于多方账户(如多重签名),PSM 包含一个 Delta 提案工作流。此功能支持不同签名者之间的协调,允许他们提出状态更改,收集其他参与者必要的签名,并仅在达到阈值后最终确定 delta。
与 PSM 的交互通过使用 Falcon 签名的自定义身份验证方案进行保护。请求必须包含 x-pubkey、x-signature 和 x-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-For 和 X-Real-IP 头来绕过速率限制 |
| ⚠️ 可利用 | 尝试在不使用 PSM 密钥的情况下在 PSM 上创建账户 |
| ⚠️ 可利用 | 文件系统模式下的路径遍历测试 |
| ✅ 安全 | 尝试在没有所有权或授权的情况下配置账户 |
| ✅ 安全 | 尝试仅使用其公共承诺配置账户 |
| ✅ 安全 | 验证直接与 Miden 网络交互的能力,绕过 PSM |
| ✅ 安全 | 验证所有敏感端点是否严格执行身份验证 |
| ✅ 安全 | 评估头部认证机制的跨账户访问 |
| ✅ 安全 | 验证 x-pubkey 和 x-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)的提案。
攻击者以 账户 A 身份进行身份验证。
受害者(账户 B)有一个带有承诺哈希 TARGET_HASH 的待处理提案。文件存储在 .../storage/ACCOUNT_B/proposals/TARGET_HASH.json。
攻击者使用以下参数调用 PUT /delta/proposal 端点:
account_id:ACCOUNT_Acommitment:../../ACCOUNT_B/proposals/TARGET_HASHcredentials:账户 A 的有效签名。请注意,攻击者必须省略 payload 中目标哈希的 0x 前缀。服务器的逻辑只有当整个字符串以 0x 开头时才会删除它。由于 payload 以 ../ 开头,因此绕过了前缀删除,并直接附加了 .json。
服务器构造路径:.../storage/ACCOUNT_A/proposals/../../ACCOUNT_B/proposals/TARGET_HASH.json
文件系统将此路径解析为受害者的提案文件。服务器读取文件,附加攻击者的签名,并将其写回,成功修改了受害者的数据。
请考虑验证 commitment 参数,以确保它是一个表示哈希的有效十六进制字符串,然后才能在文件路径操作中使用它。或者,考虑使用安全的路径连接机制,防止遍历到预期目录之外。
更新: 已在 pull request #133 中解决。
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-For 和 X-Real-IP 头,而没有验证请求是否来自受信任的代理。
当服务器直接暴露在互联网上(绕过任何反向代理)时,攻击者可以在每个请求上使用不同的伪造 IP 地址来伪造这些头,从而有效地完全绕过基于 IP 的速率限制。在每 IP 每秒 10 个请求的默认突发限制下,攻击者可以通过改变伪造的头来实现明显更高的吞吐量。
严重性取决于部署配置。如果服务器始终部署在反向代理之后,并且该反向代理会剥离或覆盖客户端提供的代理头,则此漏洞不可利用。
考虑实现受信任的代理配置,仅当直接连接 IP 与配置的受信任代理地址列表匹配时才信任 X-Forwarded-For 和 X-Real-IP 头。或者,考虑文档说明在经头净化反向代理或 WAF (Web Application Firewall) 之后部署是安全要求。
更新: 已在 pull request #136 中解决。团队表示:
我们对生产部署的计划是实际在服务器中设置一个代理,该代理将使用
PSM_TRUSTED_PROXY_IPS环境变量列入白名单,这样X-Forwarded-For和X-Real-IP就可以被信任。
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 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!