Solana 安全实践指南
ScaleBit
2024-06-17 20:09
订阅此专栏
收藏此文章




Solana 是一个高性能的区块链,旨在促进去中心化应用程序和加密货币的发展。它采用了一种独特的共识机制,称为历史证明(PoH),并与权益证明(PoS)的效率相结合,以提高网络的可扩展性。通过这种混合协议,Solana 实现了显著的速度和效率,在保持较低交易成本的同时,每秒处理数千笔交易。Solana 的架构不仅确保了快速处理,还保持了高度的安全性和去中心化。Solana 的创新方法体现在其支持智能合约和去中心化金融(DeFi)应用的能力上,使其成为区块链领域的有力竞争者。



当您开始深入 Solana 开发时,考虑以下建议至关重要。这些建议对于优化代码、增强安全性以及与 Solana 的独特功能相协调至关重要。通过遵循这些最佳实践,开发者可以提高代码效率,加强安全协议,并充分利用 Solana 架构的独特元素。遵守这些指南确保开发者能够充分利用 Solana 的高吞吐量和低延迟能力,为创新和可扩展的去中心化应用程序铺平道路。


0X00:BEST PRACTICES FOR SOLANA SECURITY


CHECK THE ACCOUNT


在 Solana 中,由于在调用 Solana 程序时所有账户都作为输入提供,用户可以提供任意账户,并且没有内置的机制来阻止恶意用户使用伪造数据这样做。因此,Solana 程序必须检查输入账户的有效性。


1.Account ownership validation


在 Solana 区块链中,由于所有账户都作为输入参数在调用 Solana 程序时提供,因此用户可以自由提供任何账户。这种设计没有内置的安全机制来阻止恶意用户携带伪造数据进行操作。因此,Solana 程序必须严格检查输入账户的有效性。


始终检查所有权!这是关于 Solana 审计的最重要的一条建议。


在 Solana 中,每个账户都有一个指定的公钥,这个公钥是唯一被允许修改账户数据的标识符。如果账户的所有权被非预期的个人或实体所拥有,那么账户中就可能存在恶意数据,这将使得该账户无法安全可靠地执行交易。账户的所有权可能被篡改,这会对系统的安全性构成严重威胁。因此,验证账户所有权的完整性是确保交易安全、维护系统信任的关键步骤。



Example
漏洞代码片段:


这是一个常见的退款功能。它首先取出每个所需的账户,并将它们反序列化成实例。配置对象尤其重要。它记录了管理员信息。只有通过检查,业务逻辑才能继续执行。

但问题在于:你不知道是谁创建了配置。事实上,在这个 unpack() 函数中,它并没有检查是谁创建了账户。因此,我们总是必须检查账户的所有者是谁——确保它来自一个安全的创建者。

修复后的代码:


2.Account state validation

在 Solana 开发中,忽略对账户状态或数据的验证是开发者可能无意中犯下的一个严重错误。例如,在 Solana 的代币借贷程序中,与特定借贷市场相关的储备数据是由给定的结构定义的。储备的初始化将触发这些字段的创建。


Example

2021 年 8 月 19 日,格林尼治标准时间 12:40,Solend 智能合约发现了一处安全漏洞。攻击者利用了 UpdateReserveConfig() 函数的认证检查中的一个漏洞。这种操纵使得有未清偿贷款的账户面临被清算的风险,并且人为地将所有市场的借款年化收益率 (APY) 提高到了过分的 250%。幸运的是,Solend 团队迅速发现并阻止了这一恶意尝试,确保没有资金受到损失。尽管有五名用户因此次事件经历了清算,但他们从清算者的额外利润中得到了大约 16,000 美元的全额补偿。

漏洞代码片段:


您是否能看出问题所在?

借贷池可能与配置不匹配。之前的检查不够充分,因为攻击者能够传入他们创建并拥有的任何借贷市场,但却改变了其他借贷池的配置。除了关注账户的所有权外,我们还需要关注账户的状态和一致性。

修复后的代码:


3. Account types check

问题总是这样的:用户可以传入任何账户。当账户中的数据被反序列化成一个类型时,没有人知道字节是什么类型。这一切都取决于你如何反序列化。有时这种问题只会导致智能合约无法工作,而有时一些黑客会构造一些严重的破坏性攻击。


Example
漏洞代码片段:


我们提供了一个常见的攻击思路,假设相同的字节可以被反序列化为 TypeA 或 TypeB:

TypeA(配置)在程序中很容易创建。TypeB(用户)构造起来比较困难,因为它可能涉及到资金和权限的管理。我们将构造 TypeA 并将其转换为 TypeB。通过这种方式,我们可以使用这个创建的账户通过程序检查,并构建一些权限和资金盗窃。


当我们创建一个新账户时,我们会为该类型的账户设置一个唯一的 TYPE 字段值。我们的反序列化函数也必须验证类型,并且如果账户没有我们期望的类型,它将抛出一个错误。

0x01: General Issues

1. Integer overflow or underflow

编写智能合约时,考虑计算溢出非常重要。大多数编程语言在处理大数或高精度运算时可能会遇到溢出问题。简单地使用加法(+)、减法(-)、乘法(*)和除法(/)等基本算术运算符可能会导致无法预料的结果,特别是当运算的值超出语言数据类型可以表示的范围时。

Example
漏洞代码片段:


修复代码:


为了避免上述风险,建议使用更安全的数学库进行计算。这些库通常提供防止溢出的机制,能够处理大量运算,并确保计算结果的准确性和可靠性。例如,它们可能使用特殊的数据类型或算法来扩展数字表示的范围,或者在操作期间提供额外的检查以捕获潜在的溢出。

2. Verify Program ID

仔细识别和检查程序内的 CPI(跨程序调用)代码逻辑至关重要。这涉及确保代码在执行 CPI 时严格包含对目标程序标识符的验证检查。这种检查对于确认目标程序的真实性和完整性至关重要。它可以防止未经授权或恶意实体试图调用该程序,从而保持系统的安全性和可靠性。

Example
漏洞代码片段:


确保此验证步骤到位是安全性的一个关键方面,因为它有助于维护软件环境中跨程序交互的安全性。

修复代码: 


3. Initialization Front Running

在初始化合约管理员的权限时,可能会面临一种场景,即第一次调用此函数的地址被设置为管理员。这种方法可能面临先发制人的攻击,导致管理员的设置无法按预期执行。这种场景下的攻击可以推演成很多种情况。


Example

假设我有一个 PDA,它维护着我程序的全局状态。这就像一个单例。以下代码片段初始化它。

易受攻击的代码段:



我们应该为这些函数设置更全面的权限检查,其中初始调用与其余调用完全不同。

修复后的代码:


0x02: Real Case Scenario

1.Check Security Dependency Chain

一个函数需要和多个合约交互,就会有多个地址输入,我们需要确认这些地址的约束是否正确,更重要的是:

一旦某个类型的数据安全需要由另一个类型来保证,那么这个类型必须是安全的,否则,所有这些安全依赖都会失效。

Example

Brrr 程序漏洞允许攻击者利用未经检查的银行账户和使用虚假账户绕过抵押要求,以最低成本铸造 $CASH 代币。这损害了 $CASH 铸造过程的完整性,而这个过程本应由 Arrow LP 代币作为抵押品来保证。

漏洞代码片段:



可以看到:

1、crate_mint 检查依赖于 crate_token.mint,
2、crate_token 检查依赖于 crate_collateral_tokens。
3、但是,crate_collateral 不会在结构中进行更多检查。它可以在安全源中伪造,因此所有依赖它作为安全检查的地址都可以伪造。


一旦 type3 不安全,则所有相关地址都存在风险。
修复建议:理清安全依赖关系,并确保检查您的安全源。

2.Uncheck Status of Account

输入的地址不经过类型检查,就有可能在不相关的函数中被成功执行,从而造成严重的后果。

Example
漏洞代码片段:


对于像 candy_machine 这样的地址,它没有进行类型检查,而只检查了合约来源。这可能导致该合约创建的所有地址都通过这部分检查。一旦在 TypeA 和 TypeB 中修改了相同的字段,这可能导致资产丢失。或者如果函数没有检查用户的初始化状态,它可以利用此问题并执行所谓的重新初始化攻击。

修复建议:如果要使用地址作为参数,请确保其类型或状态,以避免函数被滥用或攻击。

3.Protocol Vulnerabilities

现在我们需要跳出智能合约,站在更高的层次来看安全问题。很多问题来自于具体的业务逻辑设计,而不是实现。协议漏洞没有一个通用的审查流程,所以我们只能关注协议本身。

我能告诉你的是,漏洞最常出现在判断语句附近。判断和跳转一般用于协议中处理一些不同的情况。这时候我们就需要往某个方向思考,考虑构造一些特殊的情况来绕过或者进入这些函数,从而可能引发异常。基于这些异常,我们可以考虑构造攻击手段。

Example
存在漏洞的代码段:

该函数用于计算市场仓位的资产价值和借入量,当账户违约时,退出。


引入允许关闭账户的新功能(具体来说,通过 close_loan_account 和 close_collateral_account 指令)导致了一个严重问题。执行这些指令时,它们会将 pos.account 重置为 Pubkey::default()。此更改导致系统忽略与其遇到的第一个 Pubkey::default() 相关的任何后续贷款和抵押品,从而导致贷款和抵押品价值计算错误。

确保彻底审查该协议。

总之,遵循 Solana 开发中的最佳实践不仅有益;它对于构建强大而高效的去中心化应用程序至关重要。通过微调代码、加强安全措施并与 Solana 的架构保持一致,开发人员可以充分利用其快速处理和最小延迟的潜力。这种对卓越的承诺为去中心化解决方案的新时代奠定了基础,这些解决方案不仅具有创新性,而且可扩展且可靠,为区块链技术的未来奠定了坚实的基础。


关于 ScaleBit

ScaleBit,BitsLab 旗下子品牌,是一个为 Web3 Mass Adoption 提供安全解决方案的区块链安全团队。凭借在区块链跨链和零知识证明等扩展技术方面的专业能力,我们主要为 ZKP、Bitcoin Layer 2 和跨链应用提供细致和尖端的安全审计。


ScaleBit 团队由在学术界和企业界都有丰富经验的安全专家组成,致力于为可扩展的区块链生态系统的大规模应用提供安全保障。


https://www.scalebit.xyz/

https://twitter.com/scalebit_


END


点击卡片,关注 ScaleBit ~

【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。

ScaleBit
数据请求中
查看更多

推荐专栏

数据请求中
在 App 打开