离子链梁雁明:智能合约Solidity安全之虑(security considerations)

Posted IONChain离子链

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了离子链梁雁明:智能合约Solidity安全之虑(security considerations)相关的知识,希望对你有一定的参考价值。

点击蓝字,轻松关注



安全顾虑


构建一款基于预期目的运行的软件是相对简单的,然而却很难验证没有人用一种不被预期的方式去使用它。(software coded as expected, but for using?


Solidity语言中,以上观点可能更显珍贵,因为你可以通过智能合约处理通证(token:这里按照“通证”解释)或者其它更有价值的事情。而且智能合约代码的每一步执行是公开的,再者,往往源代码也是对外可见的。


当然,你肯定会考虑你代码有多少处于危险状态:你可以通过开放的网络服务来对比一份智能合约,而且很有可能,合约代码是开源的。如果你仅仅只是在网络服务中存储bug了你不太重要的列表,你可能不会这么担心,但如果web服务上存储着你的账号信息,那你就应该加倍小心了。


这章节会列出一些智能合约的代码陷阱和一般的安全的建议条例,当然,肯定不会包括所有的不安全点。还有,请注意,即使你的智能合约代码是安全的,编译器或平台运行环境本身有特殊的bug也是有可能的。一份公开已知的安全相关的bug列表可以在 list of known bugs找到。,在solidity编译器的迭代过程中修复的bug是有bug修复奖励的。


一如继往的,因为是开源文档,请帮忙我们扩展这一章节(特别强调,更多的例子并不会带来负作用)


陷阱


私有信息及随机性


在智能合约中使用的任何东西都是公开可见的,即使是定义为private的本地变量或状态变量。


如果不希望被矿工欺诈,在智能合约中使用随机数是相当机智的一种方式。


重复入(re-entrancy)


任何合约间的相互作用(合约A与合约B)及转账以太币到合约B的时候,可能会使合约B在这笔交互完成前进行回调了,下面给出了一个包含bug的示例(代码片断,非完整合约代码)

离子链梁雁明:智能合约Solidity安全之虑(security considerations)


这个问题不是太严重,因为发送交易基于有限的gas,却也仍旧暴露了一个弱弱的缺陷:转账以太币常包含代码执行,所以接收者可能回调withdraw的合约。这会让接收者获得多次的退还和基本上获取所有在合约里的以太币。特别是下面的这个合约,当使用默认上寄送出剩余gascall方法会允许攻击者退还多次。

离子链梁雁明:智能合约Solidity安全之虑(security considerations)


以了避免多次入,你可以使用(Checks-Effects-Interactions)检查-作用-交互模式:

离子链梁雁明:智能合约Solidity安全之虑(security considerations)


注意,并非只有以太币转账会带来重复进入(re-entrancy)陷阱,而是所有调用另一个合约的函数都可能会导致重复进入陷阱。而且,你也必须多合景。一个合可能会修改另一个你所依的合约的



Gas限制及循


没有固定次数的循环操作,例如,依赖于值存储的循环,必须被小心处理:基于区块gas的限制,交易只能消耗一定量的gas。不管是明确的或者是基于正常的操作,循环中迭代的次数会超过区块gas的上限,这样会导致全合约在某点进入停滞状态(失速stalled)。这种情况不适用于constant标记,只会行从区块链读取数据的方法。然而,这样的功能可能会被其它合发,被当作上操作的一部分并使其停滞失速。针对此类情况,请在合约文档中进行明确说明。


送和接收以太



在执行回退方法的过程中,合约只能依赖“gas stipend”即固定可用的gas(2300 gas),这2300gas的stipend无论以哪种方式都不可能进入到存储中。为了明确你的合约能够以这种方式接收以太币,请检查回退功能的gas要求 (for example in the “details” section in Remix).

 

有一种通使用addr.call.value(x)()方法,来运送(forward)更多的gas去接收合约。本质上这种方法与addr.transfer(x)是相同的,只是种方法运送(forward)的所有剩余的gas并且打开了接收者的能力去行更加昂的操作(种方法仅仅返回一个错误码,而不会自动抛出这个错误)。可能包含回退到你可能没有考虑到的发送合约或者其它状态变更,不管是对诚实用户还是恶意攻击者,都允许极大的便利性。


如果你想通过address.transfer送以太,一些细节你需要了解:


1、如果接收者是合,会触的回退函数,反来,它也可以回调发送者的智能合


2、当堆栈调用深度超过1024时,以太币转账会失败,因为是调用者完全控制调用的深度,他们可以强制转账失败。考虑这种可能性或者使用send确保经常性的检查其返回值。更好的话,以一种接收者可以撤销或取回的模式编写你的合约作为以上方式的替代。


3、以太币转账也可能由于接收者合约需要的gas超过已分配的gas量而失败(明确使用require,assert,revert,throw要或者因为操作是昂贵了)- 运行了“runs out of gas”(OOG)异常。如果你使用带有返回值检查的transfer或者send,这可能会为接收者提供一种方式阻塞正在发送合约中的进程。再次,最好的实践是“用withdraw模式而非send模式”


堆栈调用深度


外部函数调用可能在任何时候导致失败,如果超过了1024的最大堆栈深度,这种情况下,solidity会抛出异常。恶意攻击者可能会在他们入侵你的合约之前,强制堆栈调用到一个很高的值。


请知晓,.send()堆栈被耗尽时不会抛出异常,而是返回false如低级别的方法如.call().callcode().delegatecall()与此似。



tx.origin


永远不要使用tx.origin用于授权,假如我们有如下钱包合约:

离子链梁雁明:智能合约Solidity安全之虑(security considerations)


离子链梁雁明:智能合约Solidity安全之虑(security considerations)




细节

1、在如for (var i = 0; i < arrayName.length; i++) { ... }中,i的类型是uint8,因为这是要求的最小int类型存储0,如果数组包括走过255个元素,那么循环永不会终结。


2、没有占32byte的型可能会包含”dirty higher order bit”(高位命令位),如果你要使用msg.data那么种情况就特重要了- 会致一种延伸性的风险:你可以发起交易,交易调用函数f(uint8 x)原生字节类型参数0xff0000010x00000001。两者都满足合约而且看起来像数字1就而言,但是msg.data会是不相同的,所以当你使用keccak256(msg.data)时,你会得到不同的结果。


推荐


待警告


如果编译器对你程序进行了警告,最好做一些优化改变。即使你认为这些警告没有安全暗示,但也可能会有其它问题潜藏其中(只是未被我们意识到),任何我们产生的编译器警告其实都会被小小的改动所覆盖。

也可以试着尽早通过添加pragma experimental "v0.5.0";来开启0.5.0版的安全特性。请知晓,在这种情况下,字符experimental 并不意味着任何风险方式的安全特性,它仅仅只是开启一些特性的开关,基于向后兼容,它并未是最新solidity版本的一部分。



限定以太币的数量


限制以太币的数量(或其它token),存储在一份小的合约中。如果你的源代码,编译器或者平台有缺陷,其中的资金可能会遭受损失。如果你想限制你的损失,那么就限制你的以太币数量。



保持精简与模块化


保持智能合约精简且方便理解。将不相关的功能代码编排到其它合约或类库中去。课程应用(course apply)的源码质量的一般性建议:限制本地变量的数量,函数的长度等等。清晰书写功能描述。这样其它人就可能知道你的代码意图以及代码是否与实际情况一致。


Checks-Effects-Interactions模式


大多数功能会执行一些检查(函数,range里的参数,是否发送了足够的ether,此人是否有通证token等等),这些检查是第一步要做的;

第二步,如果所有的测试通过了,那么当前合约的状态变化也应该构建好了。与其它合约的交互应该是任何功能的最后一步。


一些早些的合约做了一些延时处理,需要等待外部功能调用,再返回一个非错误的状态,这是一种重大过失,因为我们上面提到过的重复进入问题。


故障安全模式(容错模式)


使系统实现完整非中心化,必须移除所有的中间人,这是一个好想法,特别是对新的代码,包含某些容错的机制:可以在你的智能合约中增加一些功能,以实现自检查,如“是否有ether遗漏”,“token的总和是否等于合约的余额”或者其它类似的。时刻牢记你不能使用过多的gas用于这样的操作,所以在链下帮助通过可能是需要的。


如果自检失败,合约自动切换到一种“故障”模式,这样,例如可以这样,停用所有功能操作,将控制权移交给固定可信任的第三方或者转移合约为一种简单的“give me back my money“的合约。


正式验证


使用正式验证,执行一个自动数字化证明,以证明你的源代码完全实现了正式详细计划说明书。这些说明书仍旧是正式的,虽然通常比较简单。

请知晓,正式验证本身可以帮助你理解你做了什么(the sepecification)和实际实现(the actual implementations)两者间的不同。你仍然需要检查计划说明书是否是你想要的,你是否遗漏了哪些功


原文源自:https://solidity.readthedocs.io/en/latest/security-considerations.html#use-the-checks-effects-interactions-pattern


译者介绍:

梁雁明:离子链区块链资深开发工程师,多年IT技术开发经验,精通Java,Go,Nodejs等多种开发语言,对于金融、保险等有较多的项目开发经验





关注 telegram 加群了解更多

“一物一币一码” &“万物皆矿机”

点击“阅读原文”,进入离子链官网查看项目详情

以上是关于离子链梁雁明:智能合约Solidity安全之虑(security considerations)的主要内容,如果未能解决你的问题,请参考以下文章

Solidity零基础入门Solidity编写智能合约代码

Solidity零基础入门Solidity编写智能合约代码

Solidity零基础入门Solidity编写智能合约代码

openzeppelin与solidity版本对应关系

区块链Solidity智能合约与Solidity介绍

Solidity智能合约的重入攻击