智能合约审计的246个发现:自动化工具与人工审查的较量

本文基于23次智能合约安全审计的246个发现,揭示自动化工具可检测78%的高危漏洞,但近半数问题仍需人工介入,同时探讨单元测试在安全领域的局限性。

246 Findings From our Smart Contract Audits: An Executive Summary

Alex Groce
August 08, 2019
blockchain, paper-review

迄今为止,智能合约安全研究人员(和开发人员)一直因缺乏关于实际缺陷的详细信息而感到沮丧。这种局限性增加了关键智能合约易受攻击的风险,导致风险降低资源分配不当,并错失使用自动化分析工具的机会。我们正在改变这一现状。今天,Trail of Bits 披露了我们以往所有完整智能合约安全审查的汇总数据。

我们从分析中提取的最令人惊讶和影响最大的结果是:

  1. 智能合约漏洞与其他系统中的漏洞比文献所暗示的更为相似。
  2. 大部分(约78%)最重要的缺陷(那些后果严重且易于利用的)可能可以通过自动化静态或动态分析工具检测到。
  3. 另一方面,近50%的发现可能永远无法被任何自动化工具找到,即使最先进的技术显著进步。
  4. 最后,手动生成的单元测试,即使是广泛的测试,对于专家审计员能够发现的缺陷可能提供弱保护,或者在最坏情况下根本没有保护。

继续阅读本文以获取我们研究的摘要以及这些结果的更多细节。

每个人都希望防止以太坊智能合约中的灾难性漏洞。学术研究人员通过描述一些类别的智能合约漏洞来支持这一努力。然而,研究文献和大多数在线讨论通常集中于理解相对较少的现实世界漏洞利用,通常偏向于高度可见的漏洞。例如,重入漏洞被广泛讨论,因为它们导致了臭名昭著的DAO攻击,并且在某种程度上是智能合约独有的。但是,重入漏洞是真实智能合约中最常见的严重问题吗?如果我们不知道,那么我们就无法有效地分配资源来防止智能合约漏洞。仅仅理解检测技术是不够的,我们必须知道如何以及在何处应用它们。

智能合约是新的。决策者拥有的开发人员/分析师经验相对较浅,无法以此为基础行动。拥有真实数据来得出结论是至关重要的。

因此,我们收集了23次智能合约代码付费安全审计的完整最终报告中的发现,其中5次是保密的。公开的审计报告可在网上获取,并且具有信息性。我们对这些报告中的所有246个智能合约相关发现进行了分类,在某些情况下修正了原始审计分类以保持一致性,并且我们考虑了静态和动态分析工具长期检测每个发现的潜力。我们还将这些类别的频率与我们进行的15次非智能合约审计的频率进行了比较。使用付费的专家代码审计意味着我们的统计数据不会被区块链上大量相对愚蠢的合同所淹没。使用许多审计发现而不是少数被利用的漏洞,使我们能够更好地了解未来需要警惕的潜在问题。

类别频率与其他审计不同……但没有你想象的那么不同

智能合约审计中最常见的发现类型也是我们为比较智能合约和其他类型审计而检查的15次非智能合约审计中最常见的类型:数据验证缺陷在每个环境中都非常常见,占智能合约发现的36%,非智能合约发现的53%。这并不奇怪;接受应该被拒绝的输入并导致不良行为总是容易发生,并且总是危险的。访问控制是智能合约(10%的发现)和我们审计的其他系统(18%)中另一个常见的问题来源;意外过于宽松很容易,而访问控制如果过于严格也可能是灾难性的(例如,当由于合同错误,合同所有者甚至在某些状态下无法执行关键维护任务时)。

某些类别的问题在智能合约中不太常见:不出所料,在区块链抽象化通信问题和操作系统/平台特定行为变化,并且gas限制减少了自行实现加密的诱惑的背景下,拒绝服务、配置和加密问题较少发生。数据暴露问题在智能合约中也较少见;大多数开发人员似乎理解区块链上的数据本质上是公开的,因此似乎对“意外”可见性的后果误解较少。但这些情况有些特殊;对于大多数类型的发现,包括溢出/下溢和算术精度、修补、身份验证、时序、错误报告以及审计和日志记录,发现的百分比与非智能合约审计的百分比相差在10%以内。

最糟糕中的最糟糕

除了计算每个类别中有多少发现外,我们还研究了这些发现的严重性趋势;它们的潜在严重性以及攻击者利用它们的难度。我们将最糟糕的发现称为高-低:高严重性,低难度。这些问题可以使攻击者相对轻松地造成重大损害。

我们的22个类别中有许多没有高-低发现,但少数类别的高-低率超过10%:访问控制(25%高-低)、身份验证(25%)、时序(25%)、数值(23%)、未定义行为(23%)、数据验证(11%)和修补(11%)。请注意,令人恐惧的重入漏洞,虽然通常很严重(50%的重入发现是高严重性),但根本没有高-低发现,并且仅占246个总发现中的4个。

工具和自动化:我们可以做得更好

对于每个发现,我们根据我们的最佳知识确定是否可以通过自动化静态分析(例如,我们的Slither工具)使用合理的检测器且没有太多误报来潜在检测,或者通过自动化动态分析(例如,使用基于属性的测试如Echidna或符号执行如Manticore),无论是使用现成的属性如标准ERC20语义,还是使用自定义不变量。我们没有局限于当前工具,而是展望未来,并将一个发现评为可检测,如果通过显著的工程努力可以产生的工具,但不需要软件分析技术上前所未有的进步,可能找到问题。也就是说,我们问的是“我们能否编写一个工具来找到这个,给定时间和金钱?”而不是“当前工具能否肯定找到这个?”显然,这是一个有些主观的过程,我们的确切数字不应被视为最终结论;然而,我们相信基于对每个单独发现的仔细考虑以及我们对自动化工具可能性的了解,它们是合理的近似值。

使用这个标准,26%的全部发现可能可以通过可行的静态方法检测到,37%使用动态方法(尽管通常需要添加自定义属性来检查)。然而,当我们仅关注最糟糕的高-低发现时,自动化工具的潜力要好得多。虽然静态工具检测高-低发现的潜力不如动态工具(33%对63%),但四个高-低问题可能只能通过静态分析工具检测到,这些工具也更容易应用,并且需要更少的用户努力。结合两种方法,在最佳情况下,将自动检测27个高-低发现中的21个:几乎78%的最重要发现。我们对静态或动态分析工具可能有效的估计,在极限情况下,也因发现类型而有很大差异:

类别 动态 静态
访问控制 50% 4%
API不一致 0% 0%
审计/日志记录 0% 38%
身份验证 25% 0%
代码质量 0% 67%
编码错误 67% 50%
配置 0% 0%
加密 0% 100%
数据暴露 0% 0%
数据验证 57% 22%
拒绝服务 40% 0%
文档 0% 0%
错误报告 29% 14%
抢先交易 0% 0%
逻辑 0% 0%
缺失逻辑 67% 0%
数值 46% 69%
修补 17% 33%
竞争条件 6% 59%
重入 75% 100%
时序 50% 25%
未定义行为 0% 31%

当然,在某些情况下,这些百分比并不特别有信息量;例如,我们的审计集中只有一个加密发现,因此假设所有加密错误都容易通过静态分析工具捕获是不安全的。类似地,包含代码中“拼写错误”的编码错误类别可能具有更高比例的易于静态检测的问题,但我们的审计中只有少数此类问题。

我们最好的猜测是,对系统行为的重大影响(因此高严重性)和低难度(因此在某种意义上易于找到)的结合不仅对潜在攻击者有利,而且对自动化分析工具也有很大帮助。这是个好消息。持续改进智能合约分析工具的努力是非常值得的。这也是我们发布Crytic的部分动机,这是一种智能合约的Travis CI——内置支持运行静态分析(包括一些尚未在公开发布中可用的Slither检测器)以及很快的动态分析,自动对您的代码进行分析。

也许这里最重要的结果是,使用高质量的自动化静态分析是一种几乎没有缺点的最佳实践。如果您正在编写重要的智能合约,查看相对较少的误报以检测一些最关键的缺陷,几乎不需要开发人员努力,是正确的事情。

工具和自动化:没有银弹

然而,许多发现(几乎49%)几乎无法想象用工具检测到。在大多数这些情况下,事实上,工具甚至不太可能有所帮助。Slither的代码理解功能可能有助于找到一些问题,但许多问题,以及几乎四分之一的最重要问题,需要更深入地理解区块链和市场的更大背景。例如,工具无法告知您大多数抢先交易。需要人工关注的问题不仅限于明显的类别,如抢先交易、配置和文档:例如,有35个数据验证发现、12个访问控制发现和10个未定义行为发现不太可能被自动化工具检测到——其中3个是高-低发现。整整35%的高严重性发现不太可能被自动检测到。

即使在可能的最佳近未来自动化工具世界中,即使有完整的正式验证(可能需要多达9倍的开发人员努力),许多问题仍然需要人工关注。安全是一个强人工智能难题。在机器人取代我们之前,独立的专家关注将仍然是最基本合同安全的关键组成部分。

单元测试很棒……但可能不适用于此

最后,单元测试呢?我们在审计期间没有添加任何单元测试,但我们可以查看是否存在显著的单元测试是否与审计期间较少的发现相关,或者至少较少的高-低发现。当然,数据点数量太少,无法得出任何可靠的结论,但我们没有发现我们对单元测试数量和质量的估计与一般发现或高-低发现的存在之间存在任何统计上显著的相关性。事实上,我们检测到的微不足道的关系是错误的方向:更多的单元测试意味着更多的问题(高-低发现的正面关系至少较弱,这令人安慰)。我们希望并相信这只是噪音,或者是某些混淆因素的结果,例如更复杂合同的更大攻击面。虽然我们这一结果的基础受到许多警告的限制,包括我们无需详细检查每行代码就能评估单元测试质量的能力,但我们确实相信,如果更好的单元测试和较少的审计发现之间存在因果关系,且具有大而一致的效果大小,我们应该看到比我们更好的证据。

显然,这并不意味着您不应该编写单元测试!这意味着攻击者和审计员寻找的问题类型可能与单元测试帮助您避免的问题类型没有显著重叠。单元测试可能会改进您的开发过程并使您的用户更满意,但它可能无法帮助您实际上更安全。众所周知,开发人员能够想象发生的问题并编写单元测试来检查的问题,与导致安全漏洞的问题并不经常重叠。这就是为什么模糊测试和基于属性的测试如此有价值。

这里的一个关键点是,通过基于属性的测试发现的错误可以作为新的单元测试添加到您的代码中,让您两全其美。pyfakefs模块用于在Python中创建高保真模拟文件系统,最初在Google开发,是一个广泛使用的软件系统,具有相当广泛的一套编写良好的单元测试。然而,使用TSTL基于属性的测试工具 for Python 揭示了pyfakefs中100多个先前未检测到的问题(所有问题都已修复),并让pyfakefs的开发人员添加了大量新的、更强大的单元测试来检测回归和新错误。相同的工作流程对于经过良好单元测试的智能合约和Echidna可能非常有效;事实上,它可能更容易,因为Echidna在自动找出合同的公共接口方面比大多数基于属性的测试工具在与库API交互时做得更好。

敬请期待更多细节

在我们添加更多我们自己较小规模的审计并通过与其他公司执行的审计估计进行比较来验证我们的结果后,我们将发布完整结果。与此同时,使用我们的初步结果来指导您自己对智能合约缺陷的思考。

如果您喜欢这篇文章,请分享:
Twitter
LinkedIn
GitHub
Mastodon
Hacker News

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计