火星链 火星链
Ctrl+D收藏火星链
首页 > 瑞波币 > 正文

GAS:代码分析 | 以太坊硬编码常数如何解决“重入攻击”风险?

作者:

时间:1900/1/1 0:00:00

原文作者:JordanEarls

译者:徐锦程

免责声明:该文仅为技术人员个人观点,不代表Qtum基金会立场

当我在写另一篇不相关的文章的时候,接触了以太坊生态里建立的假设。本篇文章我将会讲述为什么以太坊假设是有缺陷的以及给出相应的解决方案。首先,我们需要知道以太坊假设是什么。假设的内容是为了向以太坊智能合约发送ETH,同时为了避免重入攻击,调用智能合约的gaslimit应该不多于2300。

魔力数和STATICALL

每一个使用transfer函数发送款项的现代智能合约中,都有一个硬编码的常数——2300。例如在这个简单的例子中:

contractTester{

function()external{

addresspayablepaymentAddress=0x5A0b54D5dc17e0AadC383d2db43B0a0D3E029c4c;paymentAddress.transfer(5);

}

}

Transfer位转译成EVM字节码后如下:

CALL2300,address,....

这个数字为何如此重要?这样决定是有原因的。

这曾经是防止一类被归为“重入”的智能合约漏洞最有效率的方式。重入的概念是,一个智能合约调用另一个智能合约,最终再一次调用了原来的智能合约。重入是在臭名昭著的theDAO黑客事件中被利用的主要漏洞。当时提出的解决方案不是通过改变以太坊协议来允许合约阻止这种行为,而是最终通过改变Solidity让向智能合约发送ETH的默认行为使用非常少量的gas,这样重入问题就无法再被利用。当然,也有一个副作用,这个改变让收钱的智能合约只能在日志里记录一个事件,而不能改变状态或者做任何别的事。

巴西推出生态友好型比特币ETF,交易代码为BITH11:巴西推出了一个全新的比特币ETF,交易代码为BITH11,将利用德国加密碳评级研究所(CCRI)的审计来确定BITH11投资的环境影响,使其每年将0.15%的流动资产投资于碳信用和环保技术。(Crypto Potato)[2021/8/9 1:42:59]

但最近以太坊引入了STATICCALL,作为防止重入问题的灵丹妙药。它真的是灵丹妙药么?回答是——不全是。

首先,我们试着强制Solidity在我们的测试合约的fallback函数中使用STATICCALL:

pragmasolidity^0.5.9;

contractTester{

function()externalview{

}

functionfoo()externalview{

}

}

然后编译器用以下报错奖励了我们的冒险精神:

browser/test.sol:4:5:TypeError:Fallbackfunctionmustbepayableornon-payable,butis"view".

function()externalview{

^(Relevantsourcepartstartshereandspansacrossmultiplelines).

而且,有趣的是,并没有明确的“non-payable“关键字来明确地标识一个函数为non-payable。让我们从fallback函数里去除view关键字,检查这个ABI:

开发人员编写代码使人们可利用1982年推出的Commodore 64电脑挖掘比特币:金色财经报道,一位软件开发人员编写了代码,使人们可以在最早的大众市场计算机之一Commodore 64(1982年推出的计算机)上挖掘比特币。该代码可在开发人员文件共享平台GitHub上访问。凭借0.2 h/s的哈希率,该电脑比现代挖矿硬件要慢几个数量级。[2021/4/13 20:12:08]

[

{

"constant":true,

"inputs":,

"name":"foo",

"outputs":,

"payable":false,

"stateMutability":"view",

"type":"function"

},

{

"payable":false,

"stateMutability":"nonpayable",

"type":"fallback"

}

]

所以,Solidity决定一个fallback函数的stateMutability只能是non-payable或者payable,不允许是“view”。

但我们假装Solidity不那么糟糕,而是允许这么做。然后你就可以让Solidity使用STATICCALL向一个合约的fallback转钱,一切正常了吗?还是不行。STATICCALL的设计很有些特殊。当然,你可以在逻辑中使用fallback函数,但是STATICCALL的设计用意是允许外部合约调用而没有副作用,只返回计算结果的数据。Fallback函数实际上没有返回数据的概念,虽然如果你下降到调用者和被调用者的汇编层面来看这可以实现。所以,STATICCALL在这个场景下没什么用,除非你在做什么很少见的操作。但是如果你要做些不常见的事情,为什么不写一个常规的函数调用而要用fallback函数呢?好吧,我有些跑题。

GavinWood:平行链1.0代码或将在两周后上线:10月26日,在Web3基金会、万向区块链、新链空间联合主办的Web3.0训练营项DemoDay上,波卡创始人GavinWood透露团队正在加紧平行链的开发,并希望平行链1.0代码能够在两周后上线。同时他也表示,希望使用基于这个代码的Rococo测试网可以很快发布。[2020/10/26]

STATICCALL强调的是没有副作用。这意味着你没法做以下操作:

更改状态

调用另一个会更改状态的合约

创建合约

自我销毁一个合约

在日志里记录事件

给别的合约发送ETH,或者更改一个合约的余额

因为一个合约被STATICCALL而收到ETH

你能预计到不能更改状态,因为正是从这个角度直接阻止了重入错误的发生。虽然,我没预料到在日志里记录事件也被禁止了。在日志里记录事件对智能合约来说没有可见的实际副作用。一旦一个事件被记录在日志里,一个外部的智能合约就无法看到这个状态被记录了。这完全是个空输出。你向虚空中发送了数据,但是再也不能收到那些数据,甚至不能观察到那些数据被发送过。这个副作用只有在区块链外的世界里才可见。事件通常被用来通知外部界面进入区块链,就像说“这儿发生了你可能会感兴趣的事情”。

所以,假设以技术纯粹性的名义,STATICCALL非常严格地执行了“无副作用”这条规则。无副作用里的副作用包括了只有外部可见的副作用。另外的效果当然是ETH不能在STATICCALL里进行转移。这有效地打破了它作为解决重入问题竞争者的局面。通常来说,当调用fallback函数时,你或者犯了错,或者想要给一个合约发送ETH。当一个合约收到ETH时,它通常会在日志里记录一个事件来告诉外部程序”嘿,我收到了一笔钱,你可能想要对此做点什么,给那个用户发个信息之类的”。在有神奇的2300gaslimit时,没法给另一个合约发送部分ETH,也不能更改合约内的状态,比如更新一下”预计余额“变量之类的。STATICCALL的唯一用处是从你不想送出ETH的合约的广义函数那里阻止重入攻击。

分析:许多代码类似的代币价格存在一定相关性:LongHash刊文称,研究人员收集了一大组代币的长期价格数据,并把代码类似的代币价格进行了比较,再以比特币为价格衡量单位,来观察对整体市场趋势的影响。接着他们观察了每一对代币的皮尔森相关系数(Pearson correlation),数值范围从-1(绝对负相关性)到1(绝对正相关性)不等。

事实证明,许多听上去很类似的代币对在价格上确实存在相关性。相关系数最高的一对代币是ETH和ETC ,相关性高于0.5。这个数值反映了适度的相关性。考虑到其中一个是另一个的分叉,而且有相似的logo,这就没什么好令人惊讶的了。紧随其后的是 Zcash(ZEC)和Zcoin(XZC),相关系数为0.45。两者都是以隐私为核心的代币,而且其logo都有一个字母Z ,因此这也是意料之中的。排在第三位的是 IOST(IOST)和Miota(MIOTA),相关系数大约为0.4。物联网应用是他们共同的侧重点,两者的logo都采用了黑白色。

当然,在一些情况下可能是因为一些项目的主题类似,而不是因为投资者把两个项目搞混了。毕竟,相关性并不等同于因果关系。在这里相关性并不特别强,因此绝对可能有其他原因。(LongHash)[2020/5/6]

这意味着,唯一有效阻止重入攻击,又能发送ETH、允许创造事件的方式仍然是以前的方法,使用神奇的gaslimit常数——2300。这为什么会是个大问题?这并不是在说硬编码数字在计算机科学里被认为是不好的,而是说硬编码数字实际上使一些合约会在需要以太坊周围的生态环境做一些改变时变得无法运行。

动态区块链里的常量

这个reddit帖子[1]提供了一些有用的细节,指出了以太坊之前在一次升级中增加了CALL指令和一些其他指令的最小花费。这类顾虑在未来所有gasprice调整的时候大都依然适用。NickJohnson指出“有明确gaslimit的调用非常少有“。在这句话提出的时候,Solidity会在做与transfer相等的操作时把所有能用的gas都转移到一个合约里,于是就留下了利用重入类操作进行攻击的可能性。Solidity在theDAO攻击发生后引入了2300的gaslimit,为了阻止发生类似事件。现在,公平地说,在默认情况下调用外部合约函数时,Solidity仍然会默认把所有gas都发送过去。而且文档里关于这个操作的隐患有无数警告。神奇的2300gaslimit常数仅仅强化了那个reddit帖子里指出的问题。

声音 | 业内人士:临时代码仅用作资料展示与港交所无关:关于今晚比特大陆在港交所上市的临时代码,包括90027和810004的问题。富途证券相关人士表示,临时代码是各家券商为有可能上市的公司提供资料展示平台,与港交所无关,因此会出现多个临时代码的情况。[2018/11/6]

比如说,想象一个合约有一个payable的fallback函数,这个函数使用了一些在部署的时候便宜到能在2300gaslimit内执行的opcode。但是,为了应对一次攻击或者什么之前没发现的问题,后来这些opcode的价格大幅提高了。这个合约就会变得没法使用,没法从那些没明确地将gaslimit提高到高于2300的合约那里接受ETH。更糟糕的是,明确地提高gaslimit会让那些发起调用的合约暴露在被重入攻击的危险中。所以,这个合约很可能就必须被弃用了。根据它接收ETH的实际逻辑,它很可能像被挡在了墙里一样,里面的钱也没法提取出来。

这个2300gaslimit假设不仅仅对向后兼容性有害。它也伤害了以太坊协议内的潜在的未来创新。例如,EIP-1293[2],SSTORE的净gas计量,是一个创新的协议改进,将会降低包括存储在内的许多智能合约行为的成本。它能使存储的gas花费能反映出区块链上的实际花费,这意味着当在一次执行中第二次写入一个存储键值的时候,将会花费更少gas。这符合常理,因为从区块链的角度来看,第二次状态修改几乎没有代价,而第一次状态修改就几乎给区块链支付了足够的费用。这个提案曾经被包括进了君士坦丁堡分叉,但在最后时刻被移除了[3],因为发现这会给大量现存的智能合约带来危险隐患[4]。这个判断是对的,这个改进会减少状态存储成本,进而带来重入类的攻击隐患,即使只有非常保守的2300点gaslimit。这件事件中讽刺的一点是,提案的设计实际上会减少智能合约重入保护的gas成本,而且这也是它的主要应用场景。

现在,为了EIP-1283的重入问题而提出的解决方案是EIP-1706[5]。这个提案中的改变可以总结为:净gas值测量带来的费用减少将不会在当前执行的gaslimit低于2300时执行。所以,现在这个神奇常量在以太坊共识协议里变得更加根深蒂固。这将会有效地迫使任何未来的EVM语言在合约调用时也使用硬编码的2300gaslimit来防止重入攻击的危险。

这个神奇的假设基本上断绝了存储变得更便宜的可能性,更不用提以太坊将来要修复扩展性问题以及任何别的问题。即使有一天发明一种神奇方法能将所有存储移到链下,使存储基本免费,存储的实际gas花费仍然不能低于2300,否则就会暴露在重入攻击的危险中。

可能的解决方案

我说了很多,但这确实是个很难的问题是吧?我们正在讨论区块链,有关区块链的所有技术都很难。这也是个事实,但同时,我也更倾向不认同以太坊团队在改进共识协议时刻意强调的技术纯粹性。实际上,我觉得EIP-1706因为硬编码问题不会被以太坊主网接受,它不够纯粹。我个人预测EIP-1283会被无限期延期,可能最终会加入到以太坊2.0里面。

我会怎么解决重入问题?有两个可能的方案。

第一个比较简单:再加个opcode,但是加一个有用的。我的提议是加入这个opcode:

MAGICCALLWITHOUTREENTRANCYEXPLOITS

这确实是个需要读很久的简明的名字。但是严肃的说,这个opcode的作用于CALL基本一样,除了以下改变:

允许SSTORE,就像STATICCALL那样

其他任何事情都被允许

老实说我不怎么喜欢这个解决方案。我更喜欢解决本质问题,允许重入任何智能合约。理想情况下,可以存在一个非常简单的opcode,就像这个:

KILLMEIFREENTRANT

这会在当前合约已经在调用栈里时停止执行。这个功能只需要一个老道的开发者工作一晚上就能完成,然后再需要一个白天做安全性测试。这会允许在防止重入攻击时不会涉及到存储,并且能用如下的简单代码非常低费地实现ifcallstack.exists(currentAddress)thenthrow但是说回来,我觉得这样一个不够“纯粹”的opcode永远不会被考虑采纳进以太坊。

还有更纯粹的替代性方案,比如把所有的调用栈暴露给智能合约。如果知道了调用栈里都有什么,就能简单地写一个Solidity函数来在栈里迭代,检查是否它自己的地址被包含在它里面,来证明现有的执行时在重入。而且当然,如果不在预料之中,合约就会抛出异常来阻止任何不想要的或者预料之外的行为。这也会允许在智能合约里加入其他特性。例如,想象你举办了一场智能合约能够参与的众筹。但是,你用黑名单拉黑了一些跟恐怖分子有关的智能合约。恐怖分子能简单地部署一个“过路“智能合约,然后让被黑名单阻挡的智能合约调用”过路“合约,最后就能调用你的众筹合约。如果有调用栈的信息,这种行为就能被发现。现在在以太坊里,完全无法在链上用智能合约逻辑检测这一点。

现在以太坊上阻止重入攻击的设计几乎都本身存在安全风险。

通常在合约执行的时候,一个变量会被设置成1,表明正在执行。当执行完成后,这个变量会重置成0。这样,如果你在过程中执行一个合约调用,如果外部合约想要重新进入现有合约,这个合约会看到变量被设置成了1,然后中止执行。然而,如果有些逻辑问题导致执行结束了变量没被重置回0会怎样?基本来说,这个智能合约就被隔离了,没法再被操作,因为它一直认为它正在被攻击。

重入是以太坊生态内的头号,也是被讨论得最多的问题,同时也被一些网站列为在创建智能合约时应该小心的第一号安全问题。这是导致了theDAO攻击和一些其他的攻击与异常的元凶。这也是智能合约开发者最难正确处理的最难问题之一,所有大多数智能合约就简单地从根源上杜绝了这个可能。对我来说,以太坊还没有实施什么直接的方法来阻止重入十分不可思议。相反,依靠限制很多的STATICCALL机制或者神奇的2300gaslimit常数似乎优先级更高。在我心中,这值得有一个一流的解决方案,而非一个建立在假设之上的丑陋修改。

标签:ALLGASSTACALREALLIQ TokenDexigasStarship DogeCallisto Network

瑞波币热门资讯
KEX:BKEX Global 关于下架VOKEN/USDT交易对的公告

亲爱的BKEXer:本着保护用户的宗旨,BKEXGlobal为保证交易币种的高标准,将定期对平台内的代币进行综合性审查;如项目方出现对投资者不利因素,我们将采取对应措施,并下架对应项目.

1900/1/1 0:00:00
COI:HCoin国庆7天乐RUN幸运转盘活动上线通知

尊敬的HCoin用户:HCoin将于香港时间2019年10月1日10:00-2019年10月7日18:18开启RUN幸运转盘活动.

1900/1/1 0:00:00
比特币:走出思维误区,比特币已是史上最稳定的储值工具

随着比特币在2017年一夜爆红,关于这个对很多人来说仍然略显神秘的新兴事物的故事也多了起来。不过比特币在传言中的形象,却一直没那么正面.

1900/1/1 0:00:00
BTC:行情分析:多即空空即多

鉴于最近市场低迷,大部分币都是联动大饼而行,所以我们近期单币分析以大饼为主,就不选择其他币进行解析了,币圈是一个联动率较大的圈子,多数都会走跟随,与其花费时间去分析次位币.

1900/1/1 0:00:00
OIN:SO (SesameOpen Protocol) 售卖项目10月16日上线大币网(Dcoin)

亲爱的大币网(Dcoin)用户:大币网(Dcoin)将于2019年10月16日启动SO(SesameOpenProtocol)项目IEO售卖,支持BTC、ETH、USDT购买.

1900/1/1 0:00:00
ORE:俄罗斯人起诉苹果在加密货币交易后让其“变成”同性恋

据外媒报道,近日,苹果在一起发生在俄罗斯的诉讼中成为了愤怒的焦点。原告指出,苹果AppStore的宽松政策使其被成为了一名同性恋--这在该国是不被接受的,进而对其造成了道德、精神上的伤害.

1900/1/1 0:00:00