主页 > 多语言仿imtoken钱包系统 > 您是否规避了以太坊合约的安全弱点?

您是否规避了以太坊合约的安全弱点?

多语言仿imtoken钱包系统 2023-02-24 07:36:40

以太坊合约爆仓家破人亡_以太坊智能合约安全性_合约以太坊

以太坊的许多智能合约控制着具有真实价值的数字资产。 因此,确保合约没有安全漏洞非常重要。 这些问题为大家带来了一篇2017年关于以太坊合约攻击的研究论文,帮助大家避免以太坊智能合约设计中的一些弱点可能导致的安全问题。 在这里,你还可以看到导致以太坊分叉的著名事件The DAO攻击的原理是什么。

本文档分为两部分。 第一部分介绍如果Solidity语言和智能合约使用不当可能导致问题的一些弱点; 第二部分使用一些示例来说明这些弱点如何导致问题。

我们今天还推送了另一篇背景资料文章,为不熟悉智能合约和Solidity语言的读者介绍一些背景内容。

点契约函数调用使用不当会引发问题

在 Solidity 中编写智能合约时,可以调用其他合约中的函数。 假设Alice合约中有一个ping(uint)函数,c是Alice合约在以太坊上的一个地址。 如果其他合约(或者Alice合约本身)想要调用参数为42的ping函数,有以下三种方式:

首先

以太坊智能合约安全性_合约以太坊_以太坊合约爆仓家破人亡

call 调用:通过合约地址、合约函数、函数签名和调用参数进行调用。 如果被调用函数中有修改合约变量的代码,则被调用合约中对应的变量也会被修改。

第二

以太坊合约爆仓家破人亡_以太坊智能合约安全性_合约以太坊

delegatecall与call类似,不同的是delegatecall在执行时,只使用被调用函数的代码,如果代码涉及修改合约变量,则修改调用者合约中的变量。 如果被调用函数中有d.send(amount)指令,表示向地址d转入一定数量的以太币,调用方式从被调用合约的余额中转出资金。 在 delegatecall 模式下,它将从调用者合约的余额中转移。

因此,delegatecall是一个比较危险的命令。 如果该命令加载的功能代码超出了合约编写者的控制范围,可能会导致合约中的资金被转移或合约被毁等严重后果。

第三

合约以太坊_以太坊合约爆仓家破人亡_以太坊智能合约安全性

这第三种调用方式在论文中称为直接调用。 它首先在合约中声明Alice合约需要调用的函数,然后调用。 该方法在异常处理上不同于上述两种方法。

需要注意的是,如果上述三个方法中函数名或参数类型设置错误,将会调用回退函数(fallback function)。 如果由于打字错误而输入了错误的内容,则可能会触发不应该执行的回退函数。

无气体发送

在Solidity中,如果变量rec的类型是address,那么rec.send(amount)表示合约将wei的数量转入地址rec。 (10^18 wei = 1 ether ) 在这个执行过程中,会触发地址 rec 的回退函数。 如果fallback函数执行过程中消耗的gas大于2300,会触发异常,转账失败。

异常处理

我们在背景介绍中提到,使用Solidity执行智能合约时会抛出异常,只是合约中调用函数的不同方式处理异常的方式不同。

如果合约执行过程中没有函数调用,或者只是直接调用direct call,那么当触发异常时,则认为合约执行失败,直接停止合约执行,执行过程中合约变量的传递和修改被回滚。 操作,并扣除所有交易费用。

如果通过call、delegatecall或send调用了其他合约函数,执行过程中触发的异常不会影响原函数。 也就是说,如果send触发的fallback函数执行过程中,如果gas不足导致异常,转账会失败,但原合约会成功执行。

如果对这一点理解不够充分,会误认为合约执行成功就意味着调用也一定成功,误认为没有触发异常就意味着以太币转账成功合约以太坊,这可能会导致安全问题合同。 正确的做法应该是通过函数调用返回的结果来判断执行是否成功。 并且有研究表明,28%的合同不检查返回结果。 (当然,这并不是说一定有安全问题)

重入问题

Solidity中回调函数的机制可能会导致合约调用其他函数,被调用的函数调用调用方合约的函数,造成循环。 下面是一个例子

假设区块链上有如下合约Bob,如果sent变量为false,则向给定地址发送一笔钱。

合约以太坊_以太坊智能合约安全性_以太坊合约爆仓家破人亡

Mallory是攻击者恶意构造的合约,代码如下。

以太坊智能合约安全性_合约以太坊_以太坊合约爆仓家破人亡

Bob 合约设计的初衷是如果 sent 变量为 false,则向给定的地址发送一笔钱。 但是,当钱被发送到攻击者的合约时,攻击者合约的回退函数会被触发,回退函数会再次调用ping函数,如此循环往复,直到交易手续费用完或调用深度达到上限1024次,触发异常。 但是前面说过,call调用的函数在执行过程中触发的异常不会影响原函数的成功执行。 也就是说,除了最后一次转账会失败外,之前的所有转账都会成功。

几次攻击

接下来,我们将展示几个利用上述弱点的攻击示例。

DAO 攻击

DAO 攻击是以太坊历史上最著名的攻击,窃取了价值 6000 万美元的以太币。 以太坊社区强行回滚了以太坊的硬分叉合约以太坊,导致以太坊和以太坊经典两条分叉链并存。

以下是DAO智能合约的简化版,但足以说明DAO合约的漏洞。

合约以太坊_以太坊合约爆仓家破人亡_以太坊智能合约安全性

这个合约的功能非常简单。 任何人都可以将以太币捐赠到指定地址,接收者可以提取捐赠的货币。

攻击者可以通过以下合约转移合约中的大量币。

合约以太坊_以太坊智能合约安全性_以太坊合约爆仓家破人亡

原理和上面说的重入问题完全一样。 当执行 SimpleDAO 合约的提款功能时,它会将钱转移到攻击者的合约中。 转账会触发攻击者合约的回退函数,攻击者合约的回退函数会再次调用SimpleDAO合约。 withdraw 函数形成一个循环。 当循环由于任何原因结束时,除了最后一步之外,之前的执行都不会失败。 袭击者转移了大量资金。

另外,本合约没有考虑整数溢出问题,所以有以下攻击成本较低的方案

以太坊智能合约安全性_以太坊合约爆仓家破人亡_合约以太坊

在这个合约中,攻击者设计了一个函数攻击。 当这个函数执行时,攻击合约先捐出1wei给自己,然后收回这1wei。 提现时,会触发攻击者合约的回退功能。 与之前的攻击不同,这次我们只利用了一次重入问题,即执行了两次withdraw函数。 撤回第二笔转账给攻击者后,攻击者不再调用withdraw。

所以withdraw函数中的转账操作msg.sender.call.value(amount)()发生了两次,自然而然,它的下一行也会被调用两次。 这两个调用把credit[attacker address]变成-1 wei,虚拟机会解释为2^256-1 wei。 这时,攻击者几乎可以从中提取无限量的钱。

特别是,即使withdraw函数在转账后检查send执行是否成功,也只能防止第一次攻击。

以太宝座

考虑以下每个人都争夺王位的游戏合约。 迟到者可以通过向在王位上的人支付一笔钱来代替他们,每一轮获得王位所需的钱都比前一轮高。 最后登上王位的人有额外的好处。 (未反映在合同中。)

以太坊智能合约安全性_以太坊合约爆仓家破人亡_合约以太坊

这份契约似乎没有任何问题。 事实上,当其他人支付宝座上的人(地址)时,会触发该地址(如果是合约)的回退功能。 如果宝座上的合约地址fallback函数需要的交易手续费过高,就会触发gasless send的问题,从而导致转账失败。 不过,后续更改王位拥有者的代码仍将照常执行,新人可以免费获得王位。

修改这个问题的思路看似很简单,就是将传输代码king.send(compensation)改成if(!king.call.value(compensation)())throw; 判断转账是否成功。 然而,这会导致另一个问题。 王座上的地址(合约)设置了自己的fallback函数触发异常,比如function(){throw;},没有人有能力把他赶下王座,因为所有转账结果都会失败。

以上就是本期内容。 在下一篇文章中,我们将介绍文献中提到的其他 Solidity 弱点和可能存在的问题。

参考文献:[1] Atzei、Nicola、Massimo Bartoletti 和 Tiziana Cimoli。 “对以太坊智能合约 (sok) 攻击的调查。” 安全和信任原则。 斯普林格,柏林,海德堡,2017 年。164-186。

Conflux致力于打造下一代高性能DAPP公链平台