区块链安全—详谈合约攻击(一)
1、一、合约何以智能?在前文中,我们详细的讲述了Pos、DPos、BFT等常用的落地项目中的一些共识机制。而读者在了解了共识机制的具体流程后也应该会向我一样惊共识的协议之美。在区块链中,除了共识机制以外,还有另外一种富含魅力的技术,那就是“智能合约”。智能合约的引入增强的区块链的发展轨迹,也为区块链技术带来了更多生机。而智能合约的重要性到底是如何呢?我们应该如何看待智能合约?提及智能合约,我们首先要说明的是在早期的时候,智能合约与区块链本是两个独立的技术。而区块链诞生要晚于智能合约。也就是说,区块链1.0出世的时候智能合约还没有被采纳入区块链技术。而随着区块链的发展,人民发现区块链在价值传递的过程中需要一套规则来描述价值传递的方式,这套规则应该令机器进行识别和执行而不是人为。在最早的比特币中还没有出现这种方法,而随着以太坊的出现,这种假设在智能合约的帮助下成为了可能。按照历史的发展,智能合约最早出现在了1995年,也就是说几乎与互联网同时代出现的。从本质上讲,只能合约类似于计算机语言中的if-then语句。智能合约通过如下方式与真实世界进行交互:当一个预先编好的条件被触发时,智能合约执行相应的条款,而系统通过相应的条款进行交易的执行。在区块链2.0时代到来后,区块链正式与智能合约相结合。这也使区块链技术真正的脱离了数字货币的枷锁,成为一门独立的技术。由于智能合约的引入,区块链的应用场景一下子广泛了起来。现在在许多行业中都可以看到区块链的身影。那么智能合约是什么呢?智能合约的本质其实就是一段使用计算机语言而编程的程序,这段程序可以运行在区块链系统所提供的容器中,同时这个程序也可以在某种外在、内在的条件下被激活。这种特性与区块链技术相融合不仅避免了人为对规则的篡改,而且发挥了智能合约在效率和成本方面的优势。在安全方面,由于智能合约代码放在了区块链中并且在区块链系统提供的容器中运行的,在结合密码学技术的前提下,区块链具有了天然的防篡改以及防伪造的特性。
2、二、以太坊第二次Parity安全事件1 Solidity 的三种调用函数在讲解第二次Par坡纠课柩ity安全事件之前,我们要醑穿哩侬对一些相关的安全函数进行研究分析。我们在之前的稿件中曾经对delegatecall()函数进行过详细的讲述。而今我们对其他三种函数进行更多的分析。delegatecall()函数的滥用在Solidity中我们需要知道几个函数:call()、delegatecall()、callcode()。在合约中使用此类函数可以实现合约之间相互调用及交互。而两次Parity安全事件都是由于类似的几个函数出现了问题而导致以太币被盗。所以掌握此类调用函数的正确用法也是分析区块链安全所必不可少的。而我们知道,msg中保存了许多关于调用方的一些信息,例如交易的金额数量、调用函数字符的序列以及调用发起人的地址信息等。然而当上述三种函数在调用的过程中, Solidity 中的内置变量 msg 会随着调用的发起而改变。下面我们就详细的讲解一下此类三种函数的异同点以及安全隐患。delegatecall: 对于msg方面,其函数被调用后值不会修改为调用者,但是其执行在调用者的运行环境中。这个函数也经常爆出很严重的漏洞,例如我曾经讲述的第一次Parity的安全漏洞就是因为此函数将调用者环境中的函数跨合约执行。call: 此函数为最常用的调用方式,与delegatecall不同的是,而此时msg的值将修改为调用者,执行环境为被调用者的运行环境(合约的 storage)。callcode: 同call函数一样,调用后内置变量 msg 的值会修改为调用者,但执行环境为调用者的运行环境。在实验开始前,部署合约后查看合约A、B中的变量均为temp1 = 0, temp2 = 0。现在调用语句1 call 方式,观察变量的值发现合约 A 中变量值为0,而被调用者合约 B 中的temp1 = address(A), temp2 = 100。即msg中的地址为调用者(address(A)),而环境为被调用者B(temp2 = 100)。下面使用调用语句2 delegatecall 方式,观察变量的值发现合约 B 中变量值为0,而调用者合约 A中temp2 = 100。即调用函数后内置变量 msg 的值不会修改为调用者,但执行环境为调用者的运行环境。现在调用语句3 callcode 方式,观察变量的值发现合约 B 中变量值为0,而调用者合约 A 中的temp1 = address(A), temp2 = 100。即调用后内置变量 msg 的值会修改为调用者,但执行环境为调用者的运行环境。之后我们就可以分析第二次Parity攻击事件了。
3、2 事件分析在Parity钱包中为了方便用户的使用提供了多签合约模板,而用户使用此模板可以生产自己的多方签名合约并且不需要很大的代码量。而在Parity钱包的实际业务中都会通过delegatecall函数内嵌式地交给库合约。相当于我的关机核心代码部署在服务器方,不用用户自行部署。由于多签合约的主逻辑(代码量较大),所以合约部署一次即可,不然用户全部都要在本地部署是一个很不理智的行为。除此之外,这还可以为用户节省部署多签合约所耗费的大量Gas。下面我们看一下问题代码:代码:https://github.com/paritytech/parity-ethereum/blob/b640df8fbb964da7538eef268dffc125b081a82f/js/src/contracts/snippets/enhanced-wallet.solParity 多签名钱包第二次被黑事件是一个例子,说明了如果在非预期的环境中运行,良好的库代码也可以被利用。我们来看看这个合约的相关方面。这里有两个包含利益的合约,库合约和钱包合约。
4、3防御措施这档次的Parity事件有几种预防的方式,一是智能合约摒弃自杀函数,这样的话即使黑客获得了高级权限也无法将合约移除。第二是可以进一步对initWallet、initDaylimit及initMultiowned添加internal限定类型,以禁止外部调用:
5、可以增加internal()函数来增强限制函数的作用。三、总结本次事件是在第一次Parity事件的基础之上衍生出来的。攻击者属于无意识的误操作。
6、而针对此次事件,我认为官方需要引起相应的思考,其实在问题曝光之前就有网友提到过此类问题,但是并没有引起相关的关注。所以Parity管理者应该对此进行关注。其次,在漏洞修补方面,我认为应该更加严格的赋给权限,做到完全禁止外部陌生用户的访问。在合约设计方面,我认为类似于KILL这样的危险函数就尽量不要出现。四、参考资料1https://ethereum.stackexchange.com/questions/3667/difference-between-call-callcode-and-delegatecall2https://github.com/paritytech/parity-ethereum3https://www.jianshu.com/p/8b13024bc4304https://blog.csdn.net/omnispace/article/details/792804025https://ethereum.stackexchange.com/questions/3667/difference-between-call-callcode-and-delegatecall