13.1引用
Destefanis, Giuseppe & Bracciali, Andrea & Marchesi, Michele & Ortu, Marco & Tonelli, Roberto & Hierons, Robert. (2018). Smart Contracts Vulnerabilities: A Call for Blockchain Software Engineering?. 10.1109/IWBOSE.2018.8327567.
13.2摘要
13.3 背景
在本节中,我们简要介绍区块链和智能合约技术,它们的执行环境和模型。由于我们的研究主要集中在以太坊平台,我们将以此为例,但此处介绍的概念具有普遍的有效性。
1. 分布式账本:区块链本质上是一个共享分类账,它在一个分散的对等 络中存储保存信息的事务。节点称为矿工,每个节点都保持分类帐的一致副本。事务被组合成块,每个块与前一个块进行哈希链接。这种数据结构就是所谓的区块链,如图1所示。
图13-1区块链和以太坊架构。链上的每个块由大量单个事务组成
2. 以太坊智能合约:智能合约(SC)是一种通过合约创建交易存储在区块链中的完整程序。SC由成功创建事务时生成的合约地址标识。因此,区块链状态是从地址到账户的映射。每个SC帐户都拥有一定数量的虚拟硬币(在我们的例子中为Ether),并且拥有自己的私有状态和存储。因此,以太坊SC账户通常持有其可执行代码和包含以下内容的状态:私有存储、持有的虚拟硬币(以太币)的数量,即合约余额。
用户可以使用交易转移以太币,比如比特币,还可以使用合约调用交易来调用合约。从概念上讲,以太坊可以被视为一个巨大的基于事务的状态机,其状态在每次交易后更新并存储在区块链中。
Smart Contract的源代码以与传统命令式程序相同的方式操纵变量。在最低级别,以太坊SC的代码是由每个节点中的以太坊虚拟机(EVM)运行的基于堆栈的字节码语言。SC开发人员使用高级编程语言定义合约。以太坊的语言是Solidity (类似JavaScript的语言),它被编译成EVM字节码。一旦在地址X创建了SC,就可以通过向地址X发送契约调用事务来调用它。合约调用事务通常包括:支付(到合约)执行(在以太 中)、调用的输入数据。
图13-2是SC的一个简单示例,该示例奖励任何解决问题并将解决方案提交给SC的人。
图13-2 智能合约示例
图2中合约的EVM字节码的合约创建事务将被发送给矿工。最终,交易将在一个区块中被接受,并且所有矿工将更新区块链的本地副本:首先在区块中生成合约的唯一地址,然后每个矿工在本地执行Puzzle合约的构造函数,以及本地存储在区块链中分配。最后,Puzzle(16行)的匿名函数的EVM字节码被添加到存储器中。
当合约调用事务被发送到Puzzle的地址时,默认情况下执行第16行定义的功能。 有关发件人,发送给合同的以太 数量以及调用事务的输入数据的所有信息都存储在名为msg的默认输入变量中。 在此示例中,所有者(即创建合同的用户)可以在将当前奖励发回给所有者之后,通过发送存储在msg.value中的以太币(第17行的状态)来更新奖励(第21行)。
13.4 案例研究与方法论
近期以太坊遭受了非自主的攻击,其中,一个缺乏经验的开发者冻结了多个Parity钱包监管的以太坊账户。这次被动攻击立刻被新闻媒体 道,原因在于这些被冻结的账户中存有513774.16个以太币,约合1.62亿美元。2017年11月这次攻击被称为Parity钱包攻击。其原因是库文件中的一行代码的删除。2017年6月一个类似的漏洞也被一个黑客发现,在这次攻击中,一个需多方签名的账户被一个拥有者所控制。
以太坊钱包攻击展示了一个在智能合约或区块链相关的软件开发中可能出现的典型问题。这些问题与缺乏合适标准化的区块链软件工程的最佳实践相关。
13.5代码结构与攻击分析
Parity是一种集成在浏览器中的以太坊客户端。它允许用户访问其中的以太币与对应的钱包函数。同时,它也是一个以太坊图形用户界面,提供访问以太坊 络的函数,包括Dapp。Parity也可以作为一个全节点,用户可以在自己的电脑上存储与管理区块链。它是一个非常复杂的分布式应用。
Solidity和EVM提供了三种在智能合约上调用函数的方法:CALL,CALL-CODE和DELEGATE-CALL。前者是对将在被调用合约的环境中执行的函数的调用。另外两个调用在调用者环境中执行被调用的代码。通过DELEGATE-CALL实现对以太坊的许多库调用,通常是通过部署作为库的合约来实现:合约具有任何人都可以调用的函数,例如,可以使用这些函数来更改调用的存储与合约。Solidity有一些句法结构,允许提供DELEGATE-CALL的库被其他合约定义和“导入”。但是,在EVM级别,代码库构造消失,DELEGATE-CALL和其他调用实际上被部署为智能合约方法。
Parity选择为其多重签名钱包采用代码库驱动的智能合约开发。也就是说,Parity最初部署了一个多重签名合约作为他们的库,而所有其他Parity多签名钱包都引用了单个库合约来实现其所有函数。库本身实际上是一个正常工作的多签名钱包。事后看来,它可能不应该存在。
重要的是要强调我们正在考虑的库是一个有效的钱包。但是,由于它是一个库合约而且尚未设置变量only_uninitialized,因此尚未初始化。如果在Parity部署库合约之后,它可以调用initWallet-once()来声明合约并设置未初始化的变量,包括所有者,则可以避免攻击。然后任何人都可以在库合约上调用函数initWallet。正如“黑客”所做的那样。除其他事项之外,这种调用将调用者(即黑客)设置为正在初始化的合约的所有者。值得一提的是,这样的电话是完全合法的,它只是初始化一个尚未初始化的钱包。此时,库合约的所有者(例如,黑客)可以调用任何特权函数,其中包括kill()。kill函数调用suicide(),现在被self-destruct取代。self-destruct函数将剩余的资金发送给所有者,销毁合约并清除其存储和代码。图3显示了在地址
0x863df6bfa4469f3ead0be8f9f2aae51c91a907b4上定义的Parity智能合约库的函数及其依赖关系图。
图13-3 Parity钱包依赖图
13.6最佳有效实践
智能合约安全是一个开放的研究领域。经过数十年的工程方法分析和开发,无缺陷源代码的开发仍然是传统软件开发的乌托邦。错误自由对于区块链软件开发来说更加令人生畏,这种开发不到十年前开始。在本节中,我们将讨论可能有助于减轻攻击影响的方法,这些方法来自传统软件工程中公认的最佳实践。值得一提的是,文献中突出了导致Parity攻击的漏洞。
1. 反模式:反模式是对反复出现的问题的常见反应,这种反复出现的问题通常是无效的,并且可能会产生高度适得其反的风险。我们在案例研究中确定了三种反模式,负责分析的问题。
2. 测试:测试智能合约具有挑战性和关键性,因为一旦部署在区块链上,它们就变得不可变,不允许进一步测试或升级。目前,据我们所知,没有一个Solidity测试框架,例如,用于Java语言的JUnit,意味着必须手动测试每个智能合约。智能合约的性质至少会给测试带来两个复杂因素:应用程序可能很关键,部署后更新应用程序非常困难。结果,希望使用稳健的测试技术。手动测试生成可能是一个重要组成部分,但不可避免地受到限制; 需要有效的自动测试生成(和执行)技术。
我们调查了这个案例,分析了事件的年表和智能合约库的源代码。我们发现该库的漏洞主要是由于疏忽的编程活动而不是Solidity语言中的问题。
匿名用户分两步利用此漏洞。首先,攻击者能够成为智能合约库的所有者(因为它是创建并保持未初始化),然后攻击者只做调用初始化函数。之后调用了self-destruct函数,它杀死了库,导致无法在使用库创建的智能合约上执行函数的情况,因为所有委托调用都在死智能合约库中结束。该案例清楚地表明需要面向区块链的软件工程,以防止或减轻这种情况。
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!