246项智能合约审计发现:执行摘要
迄今为止,智能合约安全研究人员(和开发人员)一直因缺乏关于实际缺陷的详细信息而感到沮丧。这种局限性增加了关键智能合约易受攻击的风险,导致风险降低资源分配不当,并错失使用自动化分析工具的机会。我们正在改变这一现状。今天,Trail of Bits披露了我们完成的所有完整智能合约安全审查的汇总数据。
我们从分析中提取的最令人惊讶和影响深远的结果是:
- 智能合约漏洞比其他文献中暗示的更类似于其他系统中的漏洞。
- 大部分(约78%)最重要的缺陷(那些具有严重后果且易于利用的)可能可以通过自动化静态或动态分析工具检测到。
- 另一方面,近50%的发现可能永远无法被任何自动化工具找到,即使技术水平显著进步。
- 最后,手动生成的单元测试,即使是广泛的测试,可能对专家审计员能够发现的缺陷提供弱保护,甚至在最坏情况下没有保护。
继续阅读本文,了解我们研究的摘要以及这些结果的更多细节。
每个人都希望防止以太坊智能合约中的灾难性漏洞
学术研究人员通过描述一些类别的智能合约漏洞来支持这一努力。然而,研究文献和大多数在线讨论通常集中在理解相对较少的现实世界漏洞利用上,通常偏向于高度可见的漏洞。例如,重入漏洞被广泛讨论,因为它们导致了臭名昭著的DAO攻击,并且在某种程度上是智能合约独有的。但是,重入漏洞是真实智能合约中最常见的严重问题吗?如果我们不知道,那么我们就无法有效地分配资源来防止智能合约漏洞。仅仅理解检测技术是不够的。我们必须知道如何以及在何处应用它们。
智能合约是新的。决策者拥有的开发人员/分析师经验相对较浅,无法基于此采取行动。拥有真实数据来得出结论是至关重要的。
因此,我们收集了23个付费智能合约代码安全审计的完整最终报告中的发现,其中五个是保密的。公共审计报告可在网上找到,并提供信息丰富的阅读材料。我们对这些报告中的所有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交互时做得更好。
敬请期待更多细节
在我们添加更多我们自己较小规模的审计并通过与其他公司执行的审计估计进行比较来验证我们的结果之后,我们将发布完整结果。与此同时,使用我们的初步结果来 informing 您自己对智能合约缺陷的思考。