热度暴涨的SOL为何狂飙4倍?开发者构建Solana项目需要注意哪些安全问题?
摘要: Solana与Visa、Shopify等实体的合作让用户和投资者看到了Solana生态与现实消费者结合的巨大潜力。
自今年年初以来,Solana的生态一直处于快速发展的状态,尤其是SOL在过去几周内大幅上涨,让大家将目光再次转移到这个老牌项目。Marinade Finance、Lido 和 Jito等流动性质押项目的推出让Solana的TVL相比年初增长了1倍。Solana与Visa、Shopify等实体的合作让用户和投资者看到了Solana生态与现实消费者结合的巨大潜力。
目前,越来越多的开发者参与到Solana的生态构建中。10月,Beosin受邀成为Solana Hackerhouse Shanghai的合作伙伴,为开发者分享安全开发经验。本文Beosin将为大家讲解开发Solana智能合约的安全实践,加强项目方智能合约的安全性。
Solana账户特点及风险
在Solana的设计中,所有的信息都存储在账户(Account)对象中,而账户类型分为三类:
1. 数据账户,用于存储数据。数据账户又分为系统所有账户和程序派生账户(Program Derived Address)
2. 程序账户,用于存储可执行程序,即用户/Solana官方开发并部署的智能合约。值得注意的是,Solana的智能合约是可以被更新/销毁的。
3. 原生账户,指Solana上的原生程序,它们是由节点在部署时生成的智能合约,普通用户无法更新/销毁,但和其它智能合约一样,可以被合约/RPC调用。
注:Solana的程序(Program)本质上和其它区块链的智能合约(Contract)类似,下文的程序等同于智能合约。
以下是Solana账户与以太坊账户的对比:
source: Beosin
从上图可以看到,Solana独特的账户模型将数据资源和执行程序分离,使其可以快速处理并发交易,但同时开发者如何处理好账户之间的关系成为了一项安全挑战。开发者需要注意以下有关账户的风险:
1. 账户依赖校验缺失
对于一个Solana项目来说,不同的业务场景会有不同的分层架构,并且分层架构之间的数据账户存在依赖关系。开发者需要非常清楚合约与数据账户之间的依赖关系并加入约束,避免攻击者通过伪造数据进行攻击。
source: Beosin
2. 账户数据校验缺失
当执行某些Instructions时,需要对账户进行校验,以确定交互的数据对象/程序是正确的。Solana的合约开发者可能会忽略对交互的数据/程序地址进行一致性检查。
如上图所示,主程序在调用外部程序前应验证外部程序的地址是否正确。否则黑客可以提供恶意的外部程序进行攻击。
Solana智能合约安全实践
开发者除了避免围绕Solana账户产生的风险,遵循以下安全实践可以提高在开发智能合约、运行项目时的安全性:
1. 设置紧急模式
任何智能合约都可能存在未被发现的漏洞,因此,项目的开发团队应在合约中设置紧急模式并制定风险应对方案,以便在风险出现时能强制暂停合约业务并修复漏洞。
impl Mode{
pub fn transform(&mut self, desired_mode: Mode) -> Result<()>{
let new_mode = match self{
Mode::Normal => match desired_mode {
Mode::Normal => None,
Mode::Paused => Some(Mode::Paused),
Mode::Terminated => Some(Mode::Terminated),
},
Mode::Paused => match desired_mode {
Mode::Normal => Some(Mode::Normal),
Mode::Paused => None,
Mode::Terminated => None,
},
Mode::Terminated => None,
};
if let Some(mode) = new_mode{
*self = mode;
Ok(())
}else{
Err(StakingError::IllegalModeTransformation.into())
}
}
}
2. 尽量使用Anchor开发框架
Anchor框架已有许多成熟的代码模版,并支持对Solana的大部分特性进行检查,可以避免很多易忽略的细节错误,比如跨程序调用时使用Program<'info, T>类型,Anchor会帮助开发者检查调用的外部程序的地址。因此,对于Solana的合约开发者来说,我们建议在大部分的情况下考虑使用Anchor框架进行合约开发。
3. 注意数学相关问题
由于Rust在处理整数除法时会约去小数部分,因此在做复杂计算时开发者需要留意结果的精度问题。开发者应结合实际场景对计算结果向上/向下取整,或者对计算的数据做特别处理。对于整形溢出问题,建议开发者使用SafeMath或者在Cargo.toml中添加发布模式的溢出检查。
4. 重入检查
Solana支持跨程序调用,但深度目前限制为4。此限制可以防止程序可能从中间状态调用另一程序而忽略其自身可能会被回调到的情况。因此Solana的智能合约难以被重入攻击,但还是建议开发者应当进行重入检查。
5. Solana cli关闭程序账户
开发者如果使用Solana cli关闭程序,那么需注意后续将无法通过升级或重新部署的方式还原该程序账户。
6. 注意区分类型
在Solana合约开发中,账户数据本质上只是一个字节数组,程序将其反序列化为自定义的账户类型。如果没有实现明确区分账户类型的方法,程序可能会使用到预期之外的账户数据。
#[program]
pub mod type_cosplay_insecure {
use super::*;
pub fn admin_instruction(ctx: Context<AdminInstruction>) -> Result<()> {
...
}
}
#[derive(Accounts)]
pub struct AdminInstruction<'info> {
admin_config: UncheckedAccount<'info>,
admin: Signer<'info>,
}
#[derive(BorshSerialize, BorshDeserialize)]
pub struct AdminConfig {
admin: Pubkey,
}
#[derive(BorshSerialize, BorshDeserialize)]
pub struct UserConfig {
user: Pubkey,
}
在上面的案例中,AdminConfig和UserConfig类型具有相同的数据结构。这意味着UserConfig类型可以作为AdminConfig传入。只要存储在账户数据中的公钥与签署交易的管理员匹配, admin_instruction指令就会继续执行,即使签名者实际上不是Admin。
对此,开发者应该进行鉴权然后使用Account<'info, AdminConfig> 指定admin_config类型为AdminConfig,然后使用#[account(has_one=xxx)],检查交易上下文的账户权限依赖,如下所示:
...
#[derive(Accounts)]
pub struct AdminInstruction<'info> {
#[account(has_one = admin)]
admin_config: Account<'info, AdminConfig>,
admin: Signer<'info>,
}
...
7. 对智能合约进行审计
智能合约审计是通过对智能合约代码进行系统的测试和审查,尽可能发现代码中的潜在安全漏洞,排除安全风险,确保代码没有业务逻辑漏洞,符合预期运行流程和结果。此前Beosin已完成对Solana生态NFT项目Space Runners的安全审计,保障项目安全运行。
https://beosin.com/audits/Space%20Runners_202205261001.pdf
总结
Solana的开发者社区正在不断壮大,Beosin之前支持的Solana Hyperdrive全球线上黑客松已经顺利开展,Beosin也为整个Solana生态提供安全帮助。由于Solana独特的账户设计和采用Rust作为开发语言,开发者在进行相关智能合约开发时,有关代码安全方面可能存在疏漏之处。开发者可以通过遵循以上安全实践,避免常见的错误和漏洞,有效提升其合约的安全性。
此前Beosin也对Solana生态相关项目被攻击事件进行分析:用一千万撬动上亿资金?Solana生态Mango协议遭受黑客攻击事件分析。
因此我们建议项目上线前,寻求专业的安全审计公司进行全面的安全审计。Beosin作为一家全球领先的区块链安全公司,在全球10多个国家和地区设立了分部,业务涵盖项目上线前的代码安全审计、项目运行时的安全风险监控、预警与阻断、虚拟货币被盗资产追回、安全合规KYT/AML等“一站式”区块链安全产品+服务,公司致力于Web3生态的安全发展,已为全球3000多个企业提供区块链安全技术服务,包括HashKey Group、Amber Group、BNB Chain等,已审计智能合约和公链主网超3000份,包括PancakeSwap、Ronin Network、OKCSwap等。欢迎点击公众号留言框,与我们联系。
评论(0)
Oh! no
您是否确认要删除该条评论吗?