Solana智能合约的安全性:为什么你应该始终验证PDA的bump种子

  • Sec3dev
  • 发布于 2022-07-07 13:10
  • 阅读 19

本文探讨了Solana智能合约中的程序衍生地址(PDA)的安全性,尤其是如何验证PDA的bump种子。PDA的bump种子可能不是唯一的,若不进行验证,攻击者可能创建伪造的PDA,从而影响合约的安全性。文章提供了解决方案,包括使用Solana的API验证bump种子,确保合约的安全。

程序派生地址(PDAs)在几乎所有Solana智能合约中都被使用。虽然有优秀的资源(例如, Solana Cookbook) 来理解PDAs,但有一个重要的警告值得特别关注:

Solana程序的PDA可以具有相同的种子和多个有效的bump,从而产生多个不同的地址(摘自 tweet,由 @armaniferrante 提供)

这具有关键的安全意义:如果不验证其bump seeds,PDA可以被伪造(通过提供不同的bump seeds)。

换句话说,请注意以下几点:

  • PDA bump seeds 不是唯一的;一个种子可以有多个bump。
  • 相同的种子和不同的有效bump可以生成不同的有效PDA。
  • 使用PDA时,你应该始终验证其bump seed。

以下是一个示例:

示例:相同的种子、相同的program_id、三个不同的bump和PDA

输出:

为什么PDA Bump Seeds不唯一?

首先让我们了解PDA是如何计算的。下面是Pubkey::find_program_address的源代码:

查看 find_program_address 源代码 在Solana的Github仓库中

它接受两个用户提供的输入:

  • seeds
  • _programid

并返回

  • 一个PDA(类型为Pubkey)
  • 一个对应的bump(类型为u8

具体来说,它调用_try_find_programaddress,在其中引入了_bumpseed

注意:对于 target_os = “solana”,使用系统调用来计算 try_find_program_address

在上面,第479行,_bumpseed 是一个u8类型的变量,值范围在0到255之间(std::u8::MAX)。

在480–491行内,从255到0的循环中,_bumpseed 被附加到输入种子以调用 _Pubkey::create_programaddress(第484行):

Pubkey::create_program_address 要么返回有效的PDA,要么返回错误

create_program_address 中,它基本上对输入的 seeds_programid 执行一系列hash操作以计算一个密钥(以下第579行):

然后,它验证计算出的密钥是否是有效的PDA,即通过检查密钥是否在ed25519椭圆曲线上(第581行 _bytes_are_curvepoint

如果计算出的密钥位于ed25519椭圆曲线,那么它是一个有效的公钥,因此不是有效的PDA,将返回错误(第582行)。

(假设输入是随机的)计算出的密钥有 ~50%的机会位于曲线上,所以 create_program_address 将返回错误,_try_find_programaddress中的循环将继续并尝试新的_bumpseed

create_program_address 返回有效的PDA时,对应的_bumpseed将也在 _try_find_programaddress 中被返回。

现在,应该清楚的是,在给定相同的种子和 _programid 的情况下:

  • _Pubkey::create_programaddress 可以返回不同的PDA,每个都有不同的bump seed
  • _Pubkey::find_programaddress 返回具有最大有效bump seed的PDA

攻击者如何利用未验证的PDA?

攻击者可能使用与目标PDA相同的种子和program_id,但不同的bump seed创建一个伪造的PDA。

考虑下面的示例:

一个示例易受攻击的PDA

deposit_account是一个可能被伪造的PDA,因为它的bump seed可以是任意的,即,通过外部数据(bump)进行验证。

如果伪造的PDA可以通过指令的内部检查,那么它可以用来掩盖目标PDA,例如,根据伪造PDA的状态而不是目标状态转移资金。

sec3 Pro 如何检测漏洞?

Sec3 Pro 是Solana智能合约的首要安全分析服务。

为了检测这种未验证的PDA漏洞,该工具首先定位所有用户提供的PDA,包括使用Anchor宏声明的PDA。然后该工具验证每个PDA的bump seed是否得到了适当的验证。例如,与由 _Pubkey::find_programaddress 返回的bump相关的等式约束。

如果任何PDA的bump seed未被验证或可以由外部输入控制,则该工具会标记潜在漏洞。以下显示了sec3 Pro在之前的示例代码中报告的 BumpSeedNotValidated 问题的屏幕截图。sec3 Pro可在 https://pro.sec3.dev.

如何验证PDA Bump Seeds?

验证PDA bump seeds有两种常见方法:

  1. 使用 _Pubkey::find_programaddress 并检查结果是否与给定的bump seed匹配。如果不匹配,则终止。
  2. 将有效的bump存储在一个账户中,使用 _Pubkey::create_programaddress 及其存储的bump

方法1:验证来自_find_programaddress 返回的bump seed

方法2:使用 _create_programaddress 验证bump seed

对于方法2,在Anchor中,使用 _bump=<stored_validbump>。例如,bump=multisig.nonce,其中nonce是创建账户时存储在multisig账户中的有效bump:

Multisig示例源代码


关于sec3(前称Soteria)

sec3是一家安全研究公司,为数百万用户准备Solana项目。sec3的Launch Audit是一项严格的、由研究人员领导的代码审查,调查和认证主网级别的智能合约;sec3的持续审计软件平台X-ray与GitHub集成,逐步扫描pull请求,帮助项目在部署前加固代码;sec3的后部署安全解决方案WatchTower确保资金安全。sec3正在为Web3项目构建基于技术的可扩展解决方案,以确保协议在扩展时保持安全。

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

0 条评论

请先 登录 后评论
Sec3dev
Sec3dev
https://www.sec3.dev/