安全与风控
审计 Solana/Anchor 程序时系统排查 6…
在审计 Solana 原生 Rust 或 Anchor 程序时,按固定流程扫描 CPI、PDA、账户校验、签名、sysvar 与指令内省六类漏洞并输出完整覆盖表。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 49/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 85/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
审计 Solana/Anchor 程序时通用
解决思路
- 平台识别:确认目标是 Solana 程序(原生或 Anchor),检查 Solana 版本(sysvar 安全需 1.8.1+),定位源码 programs/*/src/lib.rs,并判断使用原生框架还是 Anchor。
- CPI 安全审查:用 rg "invoke\(|invoke_signed\(" programs/ 找出所有 CPI 调用;对每个 CPI 检查调用前是否校验 program ID、是否可能传入用户可控的 program 账户、Anchor 中是否使用 Program<'info, T> 类型。
- PDA 校验检查:用 rg "find_program_address|create_program_address" programs/ 和 rg "seeds.*bump" programs/ 查找 PDA 用法,Anchor 中用 rg "#\[account.*seeds" programs/ 检查 seeds 约束;对每个 PDA 确认使用 find_program_address() 或 Anchor seeds 约束、bump seed 被存储并复用、不使用用户提供的 bump。
- 账户校验扫描:用 rg "try_from_slice|try_deserialize" programs/ 查找反序列化,用 rg "\.owner\s*==|\.owner\s*!=" programs/ 查找 owner 检查;对每个账户确认反序列化前校验 owner、权限账户检查签名、Anchor 中使用 Account<'info, T> 和 Signer<'info>。
- 指令内省审查:用 rg "load_instruction_at|load_current_index|get_instruction_relative" programs/ 查找内省用法,用 rg "load_instruction_at_checked|load_current_index_checked" programs/ 检查是否使用 checked 版本;确认使用 checked 函数(Solana 1.8.1+)、使用相对索引、并做正确的关联校验。
- 启用 Trail of Bits Solana Lints:在 Cargo.toml 中加入 solana-program 依赖(使用最新版本,如 1.17),并启用 Solana 专用 clippy lints(如可用)。
- 按 6 类模式逐一检测:1) Arbitrary CPI(CPI 调用中用户可控的 program ID,CRITICAL);2) Improper PDA Validation(使用 create_program_address 而未用规范 bump,CRITICAL);3) Missing Ownership Check(反序列化账户未校验 owner,HIGH);4) Missing Signer Check(权限操作未检查 is_signer,CRITICAL);5) Sysvar Account Check(sysvar 账户可被伪造,影响 Solana 1.8.1 之前,HIGH);6) Improper Instruction Introspection(绝对索引导致指令可被复用,MEDIUM)。
- 输出覆盖表:在发现项之上输出包含全部 6 行的表格,列为 # / Pattern / Verdict / Evidence;每行 Verdict 必须是 found(引用 file:line 并在下方完整写出发现)、clear(说明搜索的约束、账户类型或检查,便于复现)或 n/a(用一句话说明为何不适用,未检查不算 n/a);Pattern 5 需引用程序目标的 solana-program 版本而非省略该行;少于 6 行或 Verdict 为空即视为扫描不完整。
- 按模板撰写发现:每条发现包含标题(如 [CRITICAL] Arbitrary CPI - Unchecked Program ID)、位置(file:line 与函数)、描述、漏洞代码、攻击场景、修复建议(如改用 Anchor 的 Program<'info, Token> 类型)、参考链接。
- 按优先级排序修复:Critical(任意 CPI、PDA 校验不当、缺少签名检查)需立即修复;High(缺少所有权检查、sysvar 账户检查)需上线前修复;Medium(指令内省不当)在审计中处理。
- 编写单元测试:使用 #[cfg(test)] 与 #[should_panic] 覆盖拒绝错误 program ID、拒绝非规范 PDA、要求签名等场景。
- 编写 Anchor 集成测试:用 TypeScript 与 @coral-xyz/anchor 构造恶意输入(如传入伪造的 tokenProgram 公钥)验证交易被拒绝。
- 使用本地验证器测试:运行 solana-test-validator,部署并执行 anchor test。
- 完成审计前核对清单:CPI 安全(所有 CPI 调用前校验 program ID、不可用用户提供的 program 账户、Anchor 用 Program<'info, T>);PDA 安全(使用 find_program_address() 或 Anchor seeds、bump 被存储复用、PDA 账户对照规范地址校验);账户校验(所有账户反序列化前检查 owner、原生校验 account.owner == expected_program_id、Anchor 用 Account<'info, T>);签名校验(所有权限账户检查 is_signer、原生校验 account.is_signer == true、Anchor 用 Signer<'info>);sysvar 安全(使用 Solana 1.8.1+、使用 checked 函数如 load_instruction_at_checked()、校验 sysvar 地址);指令内省(使用相对索引做关联、相关指令间正确校验、同一指令不可跨多次调用复用);测试(单元测试覆盖所有账户校验、集成测试含恶意输入、完成本地验证器测试、启用并通过 Trail of Bits lints、输出含全部 6 行且每行带 found/clear/n/a 及理由的覆盖表)。
- 拒绝常见借口:不以“程序小所以多数模式不适用”为由省略行(n/a 只需一句话且可复核);不以“Trail of Bits lints 通过”当作全部 6 类模式的结论(需说明其覆盖了哪些模式);不以“只检查了相关模式”代替完整扫描(先完成表格再按严重性排序);不以“无发现就无需报告”为由省略覆盖表(零发现也要输出完整表格);不以“Anchor 自动处理账户校验”为由跳过(需引用具体约束,#[account(mut)] 不是所有权检查,UncheckedAccount 完全跳过校验);不以“PDA 派生即安全”为由跳过(派生不等于校验,create_program_address 未用规范 bump 会允许多个有效地址)。
适用边界 · 注意事项
- 非 Solana 程序(如以太坊/EVM 合约或其他链)不适用,本技能针对 Solana 账户模型特有漏洞。
- 没有源码或无法访问 programs/*/src/lib.rs 等程序实现时无法执行扫描。
- 仅依赖 Trail of Bits lints 通过就认为程序干净时不适用——lints 只覆盖这 6 类模式的一个子集。