如何编写一份全面的审计报告:来自实战的经验——ImmuneBytes

本文主要讨论了智能合约审计报告的重要性以及如何编写高质量的审计报告。强调了审计报告不仅要列出漏洞,还要讲述系统如何工作、在哪里出现问题以及如何修复。文章还分享了编写审计报告的实用经验,包括报告的结构、内容要点以及如何清晰地表达技术细节。

2025 年 6 月 19 日

多年来,在审计智能合约和复杂的去中心化系统时,我们了解到,发现漏洞仅仅是工作的一半。你如何处理这些发现,如何沟通它们,以及如何指导团队完成修复,这才是真正的影响所在。

一份好的审计报告不仅仅是列出错误。它讲述了一个系统如何工作、在哪里出现故障以及如何修复的故事。它成为勤勉的记录,开发人员的参考,并且常常是项目与其社区之间信任的基础。

在这篇博客中,我们想分享我们从编写、修改和重新思考审计报告中获得的实践经验,一些来自经验,另一些来自惨痛的错误。

理解目的

审计报告是审计的唯一永久痕迹。

代码可能会更改。团队可能会轮换。但报告会保留。审计报告不仅仅是一份交付物。它是一份技术文档,需要清晰地与以下几种类型的读者对话:

  • 将实施修复的核心开发团队
  • 需要了解风险状况的利益相关者和投资者
  • 有时,还有可能在你工作基础上进行构建的其他审计员

我们看到审计报告用于从筹款演示文稿到紧急事件审查的所有内容。这意味着报告必须清晰、诚实和独立。它不仅应捕获发现的问题,还应捕获方法、范围、设计假设以及风险被推迟或确认的任何领域。

我们写过的最好的报告是那些在几个月后交给别人,仍然确信第一次阅读它的人会完全理解系统、漏洞以及每个严重性标签背后的逻辑的报告。

报告必须做很多事情:告知、说服、启用和持久化。

那么,我们如何编写一份涵盖所有三者的报告呢?

从结构开始:一份好的报告的骨架

一份混乱或无组织的报告会削弱即使是最敏锐的发现。随着时间的推移,我们已经确定了一种在清晰度和深度之间取得平衡的结构:

  1. 概要
  2. 范围和方法
  3. 系统概述
  4. 发现和建议
    • 按严重程度分类
    • 链接到特定的合约/行
  5. 最佳实践与建议
  6. 审计后开发人员的回应(可选)
  7. 附录(工具、哈希值、提交 ID 等)

让我们分解一下这些。

一份好的报告的流程

一份全面的审计报告讲述了一个故事。它从审查的内容开始,解释系统如何工作,逐步介绍方法,并列出已发现的漏洞。它还反映了你的思维过程和勤勉。客户应该能够阅读它,并且不仅对他们代码的状态充满信心,而且对你分析的严谨性充满信心。

首先是一个简短的概要。这不是无关紧要的东西,它是一个诚实的快照。如果你在相对成熟的代码库中发现了多个高危漏洞,那就直说。如果协议相当安全,但可以从 gas 优化和设计清理中受益,那也写在这里。

接下来,概述范围和方法。详细说明你审查的内容(合约、提交哈希值、部署的地址)以及未审查的内容。这可以保护你和客户免受未来的误解。我们已经学会了在这里做到非常明确。有一次,一位客户认为我们审计了一个完全超出范围的代理实现。缺乏清晰度导致了生产事件期间的混乱。永不再犯!

在描述方法时,尽量不要只是简单地提到工具的名称。解释我使用了什么(手动审查、Slither、Echidna 的模糊测试等)以及原因。每种技术都有其优势。如果你手动跟踪了跨多个合约的重入路径,或者在分叉的主网环境中模拟了边缘情况交互,请注明。这样做的目的是帮助读者理解审查的深度和广度。

捕捉系统的本质

一份好的审计报告证明你了解你刚刚审计的系统。这意味着超越代码语法并深入研究协议逻辑。

本节通常在智力上要求最高。它要求你将实现抽象成一个心理模型,合约如何交互,信任边界存在于何处,假设是什么,以及状态如何转换。

如果项目具有创新的机制,例如 rebase tokens、L2 消息 relays 或可升级代理,请将其标记出来。使用图表。即使是 ASCII 图表也有帮助。

“该协议包括一个 staking contract、一个奖励分配器和一个跨链桥。资金被锁定在 L1 上,并通过 5 个签名者的 oracle quorum 释放到 L2 上。”

我们发现编写本节会迫使你仔细检查你的理解,有时,隐藏的风险就会在这里显现。

一位客户曾经告诉我们:“我对你的审计更有信心,因为很明显你了解我们的业务逻辑。” 这让我们印象深刻。

精确记录发现

这是审计报告的核心:发现

每个问题都需要比标题和严重性标签更多的信息。它应该包括对问题的清晰解释,为什么重要,如何重现,以及应该采取什么措施。

我们很早就知道不要急于这一部分。对于每个漏洞,问问自己:六个月后阅读此文档的人是否会在没有你进行通话的情况下理解它? 这是标准。

严重性评级

  • 严重 – 可能导致资金损失、永久锁定或未经授权的访问。
  • – 严重的错误,但存在限制或需要特定条件。
  • 中等 – 可能导致意外行为或中等财务风险。
  • – 不会直接影响安全性,但表明设计不佳。
  • 信息性 – Gas 优化、代码的清晰性或最佳实践偏差。

每个问题应包含:

  • 标题(例如,“EmergencyWithdraw 中的不正确的访问控制”)
  • 严重性(附带理由)
  • 描述(问题是什么以及为什么重要)
  • 重现步骤或 PoC
  • 修复建议
  • 状态(打开/已解决/已确认)

例子:

标题: 无界循环可能导致 Out-of-Gas 回滚

严重性: 中等

描述: withdrawAll() 函数迭代所有用户存款,没有上限。在压力测试中,由于区块 gas 限制,这导致了回滚。

修复: 考虑批量提款或限制每次调用的迭代次数。

不要假设,要解释

当你解释为什么某件事是一个漏洞时,客户会很感激,而不仅仅是说它是一个漏洞。

_上下文至关重要。_

描述上下文至关重要。代理模式中的存储冲突不仅仅是一个错误,它是一个具有下游影响的设计缺陷。轻微的重入向量可能仅在特定流动性条件下可利用。这种细微差别很重要。没有它,你可能会高估或低估现实世界的风险。

严重性分级也需要判断。不要只使用清单。考虑可利用性、影响和修复的难易程度。在对时间敏感的预售合约中的中等严重性错误可能比在暂停或已弃用的模块中的高严重性问题更加紧急。

同样重要的是语气。避免危言耸听或傲慢自大。目标是告知,而不是羞辱。当报告感觉像是协作而不是责备时,开发人员更容易接受。

超越漏洞:增加价值

即使代码库是安全的,几乎总是有可以改进的地方:gas 效率低下、冗余检查、非标准模式或文档不足的部分。本节不是强制性的,但尝试将其包含在每份报告中。你可以在这里提及:

  • 滥用 tx.origin
  • 对可见性修饰符的不当使用
  • 代码重复
  • 未使用的变量或事件
  • 缺少 NatSpec 文档

它表明你不仅关心代码质量,而且关心发现错误。

如果可能,我们会添加一个后续部分,跟踪问题是否已解决。将每个发现映射到客户响应:已修复、已确认或不会修复,这增加了透明度,并表明审计不仅仅是走过场。

清晰地写作

技术准确性是不容协商的,但清晰性是使报告真正有用的原因。

为博士写作和为从业者写作是有区别的。最好的审计报告是高级工程师可以根据其采取行动而无需交叉引用三篇学术论文的报告。

避免为了行话而使用行话。你不是在给其他审计员留下深刻印象,你是在帮助开发人员修复他们的代码并让用户安心。

不要写:

“访问控制逻辑容易受到竞争条件的影响,该竞争条件是由于在重入上下文中异步函数解析导致对可变状态的非原子更新而引起的。”

只需说:

“该函数可以在状态完全更新之前重新进入,从而允许攻击者利用不一致的逻辑。”

保持清晰、简洁和正确。

此外,格式化报告以提高可读性。一致的标题、问题编号、内联代码片段和超链接引用都可以让读者更轻松。并且始终包括附录、工具版本、提交哈希值、自定义测试脚本,以便可以验证或复制工作。

要避免的常见错误

  • 在报告中塞满低影响问题 – 优先考虑重要的事情。
  • 模糊的建议 – “使用更安全的模式”对任何人都没有帮助。
  • 忽略 gas 使用或测试覆盖率见解 – 这些是审计质量的一部分。
  • 忽略提及_未_在范围内的内容 – 始终将其标注出来。
  • 没有对报告进行版本控制 – 始终包含报告日期和版本。我们曾遇到几个月后返回的报告,其中包含更改并且无法跟踪增量。

最后的想法

编写审计报告并不光彩。但它可以说是最具影响力的。一份清晰、彻底的报告可以防止数百万美元的损失。一份草率的报告会产生虚假的安全感,或者更糟的是,导致遗漏漏洞。

在这个代码即法律且漏洞利用快速发展的领域,审计报告通常是勤勉的唯一书面记录。它是你思维过程、你的标准和你的可信度的反映。

所以花点时间。做得认真点,因为它确实很重要。

  • 原文链接: blog.immunebytes.com/202...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
ImmuneBytes
ImmuneBytes
Stay Ahead of the Security Curve.