11 月 14 日至 17 日,Solana Hacker House 在香港举办为期四天的线下活动,由 Solana Labs 核心工程师提供现场指导,同时还有来自其他生态系统团队的导师支持。慢雾安全团队的公链安全负责人 Johan 受邀在活动中做了一个主题为 Advanced Security and Auditing Strategies for Solana Blockchain 的演讲,从开发中的安全考量、Solana 中的安全事件、如何做智能合约安全审计三个方面为大家讲解 Solana 生态安全和审计策略。
账号的公私钥对
Solana 的 Public key 由 Ed25519 算法生成,该算法安全快速,有一定的抗量子计算的能力,且生成的签名具有确定性,可以更好地抵抗签名延展性攻击。
Solana 账号
Solana 的设计理念和 Linux 的文件有一定的相似性,账号可以用于存储数据,也有严格的所有权和读写权限控制。Linux 通过 Path 来指向要读写或执行的文件,而 Solana 通过 Public key 指向要读写或运行的账号,不同的是 Solana 账号需要为存储空间支付费用。
Solana 账号具有以下特性:
账号是用于存储数据的
账号的最大存储空间是 10 MB
存储空间需要支付租金
默认创建账号时的 owner 是 System Program
Programs 是存储在账号中的可执行程序
Programs 可由用户和其它 Programs 调用
Programs 是无状态的
PDA 账号最大存储空间是 10 KB
程序账号 & 数据账号
由于 Solana 的 Programs 是无状态的,因此它在存储数据的时候就需要创建用于存储数据的账号,即 PDA 账号,PDA 账号的 owner 可以将数据以特定的结构序列化后存储在 Data Account 里。
程序指令
下图是 Solana 智能合约的调用示意图,和以太坊的智能合约不同的是,Solana 一个 Transaction 里可以通过 Instruction 调用多个 Program。客户端调用 Program 时,首先需要传入几个关键参数(Program ID, Accounts, Data) 来构造 Instruction,这个调用非常简单灵活,但也因此存在非常多的安全引患。
账号的校验就是其中一个严重的安全隐患,智能合约需要非常严谨地设计账号的关联关系,我们看下图的结构,key 指向账号本身,lamports 需要校验租金是否足够,data 要根据特定场景解析出具体的值进行校验,owner 通常与 Program ID 相关联,还有要校验签名、账号是否可写等等。
程序运行时的约束
程序在运行时受到 runtime 的约束,例如:
只有账号的所有者才能更改所有者
未分配给 Program 的账号不能减少其余额
只读和可执行账号的余额可能不会更改
只有所有者可以更改账号大小和数据
账号被添加可执行属性以后就无法回退
任何人都无法修改与帐号相关联的 rent_epoch
重入保护
这是 runtime 对智能合约的调用做的另一个限制。在以太坊创建早期,发生过一起知名的重入攻击事件,导致大量投资人的资金被盗,Solana 吸取了这个教训,禁止了合约的重入。
账号安全编码注意事项
Key 校验:检测传入的账户是否是预期的账户,例如 Sysvar 账户
Owner 校验:验证数据编写者是否具有适当的授权
Signer 校验:认证发起请求的调用者的身份
Program ID 校验:确认执行环境是否符合预期条件
PDA 校验:验证数据完整性,以确认其来自可信任的来源
Lamports 校验:检测用于存储数据的租金的充足性
Data 校验:分析数据格式,以确保符合预期结构,比如验证 SPL-Token 规范
Solana 上的资产:代币
由于 programs 是无状态的,所以 token 并不能只在单个账号中实现,token 主要由这几部分组成,token-program, mint-account 和 token-account。简单地说,发行一个 token,就是创建了一个 mint-account,接收一个新的代币,就是创建了一个 token-account。
mint-account 中存储了基本的代币信息,比如 mint-authority, supply, decimals, is_initialized, freeze-authority。
token-account 中记录了用户持有的代币信息,比如 mint, owner, amount 等,还有账号的授权信息,如 delegate, delegate_amount 等。
下图是代币各账号之间的关系图,token-program 可以无限地创建代币,用户也可以拥有无限的 token-account,他们的 account owner 都是 token-program。
NFT 也一种 SPL-token,目前默认使用的是 metaplex 创建的标准。相比同质化代币,NFT 的主要特点是它的 supply 为 1,decimals 为 0,代表 NFT 独一无二且不可分割。
值得一提的是,token 的授权模型中一次只允许授权给一个账号,相比 ERC-20 标准,这种方式大大降低了代币被盗的风险。
以下是 SPL-token 在编程中需要注意的事项:
Token Program ID 校验:确认代币是由官方项目发行的
Mint 校验:确保收到的是正确的代币
Mint Authority 校验:保护代币铸造权利,利用如多重签名等方法以防止私钥滥用
Decimal 校验:验证代币的小数精度,以确保准确的交易
Amount 校验:实施范围验证,以防止溢出并保持计算准确性
Freeze Authority 校验:确保 NFT 的发行权限被不可逆地终止
据慢雾区块链被黑档案库(https://hacked.slowmist.io) 统计,截至目前,Solana 生态中的损失已超过 5 亿美元。
接下来我们简单介绍一些 Solana 中发生过的黑客攻击事件,让大家了解黑客是如何攻击 Solana 应用的。
1. Wormhole 跨链桥被攻击
这个事故的主要原因是传入的一个系统账号没有校验,黑客使用伪造的账号,使用精心构造的数据填充账号,使得智能合约错误地给黑客增发了代币,此次事件造成了 12 万个 ETH 的损失。
2. Nirvana 遭闪电贷攻击
该项目没有开源,只能从浏览器进行分析,我们可以看到黑客从 solend 里借出了大量的 USDC,操控了代币的价格,进而获利 300 多万美元。由此可见,并不是项目不开源就不会被黑客攻击,有经验、有耐心的攻击者还是可以分析出协议中可能存在的安全漏洞,因此开发团队不能有侥幸心理。
3. 大规模盗取私钥的事件
2022 年 8 月,我们发现 Solana 社区有大量的用户被盗取私钥,奇怪的是调查发现他们使用的并不是同一个钱包,而且使用习惯也不相同,所以该事件的原因并没有定论。但是我们发现有一些钱包由于使用了 sentry 组件,导致私钥被上传到服务端。慢雾安全团队曾写过慢雾:Solana 公链大规模盗币事件的分析,感兴趣的朋友可以点击查看。
4. 代币假充值攻击
这是一个链下的安全问题,攻击者创建一个 token-account,往这个账号里转移代币,然后转出,最后把这个 token-account 的 owner 转移给交易所的充值地址,交易所在检测 token-account 的链上记录时,会误认为这个账号有真实的充值,于是为这个恶意用户进行充值,导致了攻击的发生。
安全并不是只有在技术上做好就行,因为安全的环境在经过时间的变化后可能会有新的变量引入,这些变化导致系统变得脆弱。我们认为做好安全需要考虑三个重要因素:要有安全预算、要进行持续的审计、要有高管直接负责安全工作。
智能合约安全审计是一个严谨过程,所需要掌握的知识和技能也很多,慢雾安全团队在 Github 上开放了智能合约安全审计技能树(https://github.com/slowmist/SlowMist-Learning-Roadmap-for-Becoming-a-Smart-Contract-Auditor) 和 Solana 智能合约安全最佳实践(https://github.com/slowmist/solana-smart-contract-security-best-practices),欢迎感兴趣的朋友移步到 Github 上阅读。
慢雾安全团队在智能合约安全审计领域里耕耘多年,Badwhale 是慢雾安全团队独家且沉淀多年的商业系统,为数十个平台持续服务多年,已避免了预估几十亿美金资产的假充值风险,也支持 Solana 的假充值检测。目前,慢雾安全团队已审计过数十个 Solana 的项目,如果项目方有审计方面的需求,欢迎与我们联系。
最后,感谢 Solana Hacker House 的邀请,希望通过本次分享帮助大家更好地认识了 Solana 的生态安全和审计策略。