关键词:Solana 智能合约、合约漏洞、所有权检查、签名验证、整数溢出、invoke_signed、账户混淆、合约审计
在过去 12 个月里,我们为数十条 Solana 核心程序做安全审计,成功阻止了价值约 10 亿美元资产的潜在损失。今天把最易被忽视的 5 大高频漏洞汇集成一篇千字指南,帮你在编码阶段就把坑填平。哪怕只做「看完贴代码」这最后一步,也比 0 加固安全的多。
👉 Solana 开发者必备自查清单,照着改就能直降 80 %攻击面
1. 缺失所有权检查(Missing ownership check)
漏洞速写
Solana 账户本质是一串字节,任何人都能提交。必须对非用户完全控制的账户执行 AccountInfo::owner == expected_owner,否则攻击者可替换任意数据。
快捷修复:把「未经校验的 AccountInfo」封装成已验证的自定义类型,再在业务逻辑中全程使用这一类型。
示例对比
错误写法
let config = ConfigAccount::unpack(next_account_info(account_iter)?)?;
if config.admin != admin.pubkey() { ... } // 只验证内容,没验证归属安全写法
let config_info = next_account_info(account_iter)?;
if config_info.owner != program_id {
return Err(ProgramError::InvalidConfigAccount);
}
let config = ConfigAccount::unpack(config_info)?;2. 遗漏签名验证(Missing signer check)
漏洞速写
任何应该「受限调用」的指令,必须检查 AccountInfo::is_signer。否则攻击者可把目标地址硬塞进参数,而你误以为操作已获授权。
快速自检:在审查函数时,把所有“修改权限/资金”指令逐行问一次:谁签名?我验了吗?
示例对比
错误写法
if admin.pubkey() != config.admin { ... } // 只比对,不验签安全写法
if !admin.is_signer {
return Err(ProgramError::MissingSigner);
}3. 整数溢出 & 下溢(Integer overflow & underflow)
漏洞速写
Solana 最终以 --release 模式打包,整数溢出 不会 panic,而是静默绕回。务必使用 checked_add、checked_sub、checked_mul,以及 TryFrom 做类型转换。
实战片段
错误写法
if amount + FEE > user_balance { ... } // 可能在 release 模式下绕回安全写法
let total = amount.checked_add(FEE)
.ok_or(ProgramError::InvalidArgument)?;
if total > user_balance { ... }4. 放任任意程序调用(Arbitrary signed program invocation)
漏洞速写
invoke_signed 的 program_id 由用户传入——若不校验,攻击者可替换为“假 SPL”,一次交易即可把金库搬空。
快速修复:在调用前加一行 if token_program.key != &spl_token::id() { return Err(...); }。
真实后果
某 DeFi 协议曾因此损失 20+ 万 USDC,事后回滚才发现只是少写 ==。
5. 账户类型混淆(Solana account confusions)
漏洞速写
账户数据只是一串字节,不会在链上自动映射到结构体。若合约用 unpack_config 却拿到 User 数据,字段错位即可直接导致权限绕过。
根因分析
- Solana 无内置“类型标签”;
- 升级后数据结构变化,旧数据依旧存在链上;
- Owner 相同≠数据格式正确。
修复思路
- 在结构体第一字节写入全局唯一标识符
TYPE; - 所有
unpack_*函数先比对TYPE; - 升级时引入 新版本类型,避免混用旧账户。
pub struct Config {
pub tag: u8, // 0xCA
pub admin: Pubkey,
pub fee: u32,
...
}常见疑问解答(FAQ)
- Q:每个账户我都写检查,交易会不会太占 Compute Unit?
A:现代 Solana 节点 1.4M CU 已经非常宽松;一次额外Pubkey::eq仅 ~200 CU,比漏洞后果便宜得多。 - Q:项目正在线上,能否对已部署账户补加 TYPE 字段?
A:可以创建新版本的 PDA,用迁移指令把旧账户余额搬到新数据格式;务必保留旧账户只读避免回滚风险。 - Q:整数溢出只在极端金额出现,真的需要全量检查吗?
A:黑客可使用闪电贷放大金额到u64::MAX,平日测试无法触发不代表生产安全。 - Q:自己写了自动化测试还能跳过签名/所有权校验吗?
A:测试网可以造假签名或程序,生产主网不行。务必在测试里用畸形交易跑负面用例。 - Q:我同时在做 EVM 项目,Solana 有哪些额外注意点?
A:Solana 数据模型是“账户即文件”,没有 msg.sender 概念,必须手动验证所有输入账户的身份、类型、签名。 - Q:有没有工具可以快速扫描前述 5 种漏洞?
A:Mangosrum、Soteria 等静态分析器能检出 70 %+ 常见模式,但仍不能取代人工审计与边界测试。
👉 最实用的 Solana 静态扫描器在线体验,一键帮你揪出高危函数
小结 & 下一步行动
把上面 5 条写成「MR-Checker」嵌入 CI:
- 每次
build-bpf前先跑 Rust 单元测试; - 设置
#![deny(unsafe_code)]; - 用
Anchor的同学可以在Accountsstruct 内封装所有权/类型/签名校验,让框架自动生成。
漏洞列表并未穷尽,若合约涉及多线程、跨程序调用或复杂业务逻辑,仍需 深度人工审计。后续我们将逐篇深入解析回调陷阱、PDA 碰撞及权限最小化原则——保持关注!
对上手指南或审计支持有疑问?欢迎在下方评论区留下 具体代码片段,我们将抽取典型案例做深入剖析。