安全与风控
审计 Substrate/Polkadot pallet…
在审计或上线前检查 Substrate/FRAME pallet 与 runtime 代码时,按 7 类平台特有漏洞模式逐项扫描并输出完整覆盖表。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 59/100 ×0.15
适用场景
审计 Substrate/Polkado特定
解决思路
- 先做平台识别:确认代码使用 Substrate/FRAME 框架,检查 Substrate 版本(v0.9.25+ 才有事务性存储),定位 pallet 实现(pallets/*/lib.rs)与 runtime 配置(runtime/lib.rs)。
- 对每个 #[pallet::call] 可调度函数逐项分析:算术是否用 checked/saturating、是否有 unwrap/expect/越界索引、权重是否与成本成比例且输入有界、origin 校验级别是否合适、是否先校验后写存储。
- 做 panic 扫描:用 rg "unwrap\(\)" pallets/、rg "expect\(" pallets/、rg "\[.*\]" pallets/、rg " as u\d+" pallets/、rg "\.unwrap_or" pallets/ 搜索易 panic 模式。
- 做算术安全检查:用 rg " \+ |\+=| - |-=| \* |\*=| / |/=" pallets/ 找直接算术,再用 rg "checked_add|checked_sub|checked_mul|checked_div" pallets/ 和 rg "saturating_add|saturating_sub|saturating_mul" pallets/ 确认是否改用安全方法。
- 做权重分析:运行 cargo test --features runtime-benchmarks,核对权重与计算成本是否匹配,检查输入参数是否有上界,审查权重计算函数。
- 做 origin 与权限审查:用 rg "ensure_signed" pallets/ | grep -E "pause|emergency|admin|force|sudo" 找特权操作,确认应使用 ensure_root 或自定义 origin(ForceOrigin、AdminOrigin),并用 rg "ensure_root|ForceOrigin|AdminOrigin" pallets/ 核对。
- 做测试审查:确认所有可调度函数有单元测试、有 fuzz 测试覆盖 panic 条件、有 benchmark 计算权重、有 try-runtime 迁移测试。
- 逐项检查 7 类漏洞模式:1) 算术溢出(直接 + - * / 在 release 下回绕,必须用 checked_* 或 saturating_*,影响余额/奖励/手续费计算);2) Don't Panic(panic 会导致节点停止出块,禁止 unwrap/expect/无边界检查的数组索引,所有用户输入用 ensure! 校验);3) 权重与费用(权重错误会招致垃圾攻击,可变成本操作使用固定权重会导致 DoS,必须用 benchmarking 框架并为所有输入参数设上界);4) Verify First, Write Last(v0.9.25 之前先写存储后校验会在出错时残留,模式应为 validate → write → emit event,升级到 v0.9.25+ 或手动加 #[transactional]);5) 无签名交易校验(校验不足会招致垃圾/重放攻击,优先用签名交易,若必须无签名则校验参数、防重放、认证来源);6) 坏随机数(pallet_randomness_collective_flip 易被合谋操纵,必须用 BABE 随机数 pallet_babe::RandomnessFromOneEpochAgo,用 random(subject) 而非 random_seed());7) 坏 origin(ensure_signed 会让任意用户执行特权操作,必须用 ensure_root 或自定义 origin,并在 runtime 中正确配置 origin 类型)。
- 按严重度排序修复:Critical(立即修复)包括算术溢出、panic DoS、坏 origin;High(上线前修复)包括权重错误、verify-first 违规、无签名校验问题;Medium(审计中处理)包括坏随机数。
- 补充测试建议:用 test-fuzz 做属性测试(如 fuzz_transfer、fuzz_no_panics 确保不 panic);用 cargo build --release --features runtime-benchmarks 后执行 ./target/release/node benchmark pallet --chain dev --pallet pallet_example --extrinsic "*" --steps 50 --repeat 20 生成权重;用 cargo build --release --features try-runtime 后执行 try-runtime --runtime ./target/release/wbuild/runtime.wasm on-runtime-upgrade live --uri wss://rpc.polkadot.io 测试 runtime 升级。
- 报告时先输出覆盖表,7 行必须全部存在,每行给出 verdict 与证据:found 需引用 file:line 并在表后完整写出发现;clear 需说明该模式适用且已处理,并写出你搜索的宏、origin 检查或算术方法以便复现;n/a 需用一句话给出不适用原因(如“该 pallet 不接受无签名交易”),没查过不算 n/a;模式 4 需说明目标 runtime 版本而非省略该行。少于 7 行或 verdict 为空都算扫描不完整。
- 完成前对照快速检查清单:算术安全(无直接 + - * /、全用 checked_*/saturating_*、类型转换用 try_into() 并处理错误);panic 预防(无 unwrap/expect、无无边界索引、用户输入用 ensure!、除法检查零除数);权重与 DoS(权重与成本成比例、输入有上界、用 benchmarking 定权重、无零权重昂贵操作);访问控制(特权操作用 ensure_root 或自定义 origin、ensure_signed 仅用于用户级操作、runtime 正确配置 origin、生产前移除 sudo pallet);存储安全(用 v0.9.25+ 或手动 #[transactional]、先校验后写、成功后发事件);其他(无签名交易尽量改签名、若必须则校验/防重放/认证、用 BABE 随机数而非 RandomnessCollectiveFlip、用 random(subject) 而非 random_seed());测试(所有可调度函数有单元测试、fuzz 测试找 panic、生成并核对 benchmark、try-runtime 迁移测试、输出含全部 7 行且每行有 found/clear/n/a 及理由的覆盖表)。
- 拒绝以下合理化借口:不要因“pallet 小所以多数模式不适用”而省略行,n/a 只需一句话且可复核;不要因“编译无警告”就认为算术没问题,release 构建会静默回绕;不要只检查“自认为相关”的模式,判断哪些模式相关本身就是扫描的一部分;不要因“无发现”就不输出覆盖表,零发现也要输出完整表;不要以为 ensure_signed 就是 origin 检查,它只证明有人签名而非对的人签名;不要以为“权重已 benchmark 就正确”,benchmark 只覆盖它执行的路径,最坏情况依赖存储大小或无界循环的仍是模式 3。
适用边界 · 注意事项
- 非 Substrate/FRAME 的 Rust 项目或非 Rust 链上代码,本技能的平台特有漏洞模式不适用。
- 没有 pallet/runtime 源码、只有编译产物或接口文档时,无法逐函数做可调度分析与覆盖表。
- 未安装 cargo-fuzz、test-fuzz、benchmarking 框架或 try-runtime 时,测试与权重相关步骤无法执行。