主页 > imtoken钱包安卓版下载步骤 > 深度 | 分析以太坊智能合约安全漏洞的解决方案是什么?

深度 | 分析以太坊智能合约安全漏洞的解决方案是什么?

区块链是一种新型的分布式系统体系,它采用P2P点对点网络通信、块存储、分布式算法共识和加密算法来防止篡改。 从本质上讲,区块链可以看作是一个由所有网络节点共同维护的分布式数据库。 与传统的分布式数据库相比,区块链以其完备的数据备份、公开透明不可篡改的网络、完备的信息溯源和弱信任模型等特点,更适用于对去中心化信任有明确需求的应用场景。 特别是典型的区块链应用场景包括数字支付、产品溯源、版权保护、供应链金融等。在区块链系统中,智能合约在实现去中心化应用部署和扩展应用功能方面发挥着极其重要的作用。 与传统应用相比,区块链系统具有公开、透明、可执行、不可篡改、不依赖第三方等特点,能够满足各种去中心化应用。 场景需求,包括近年来发展迅速的去中心化金融(Defi)项目。

160155651149960055.jpg

去中心化金融 (DeFi) 是一种成功的金融范例,截至 2021 年 1 月 15 日资本化超过 45 亿美元,它利用基于区块链的智能合约来确保其完整性和安全性。 去中心化金融是交换、借贷代币的新发展领域。 通常,这些指令是由智能合约(而不是单个集中式法人实体或个人)监管和/或由多方分散治理机制(例如 DAO)控制的“收益”或收入流。 去中心化金融(DeFi)——出现在现有区块链平台之上。 这个新领域的组成部分包括与借贷、资产交易和衍生品市场相关的领域。 智能合约作为一种去中心化的应用,管理着大量的数字资产,这也使得它们容易受到各种攻击。

本文将从三个维度阐述智能合约的安全问题:一是以太坊架构各层漏洞导致的智能合约安全问题。 其次,将讨论最近针对 DeFi 和智能合约的各种攻击。 最后,我们将讨论现有可用工具和其他有效做法如何最大限度地减少此类攻击。

以太坊架构概述

在讨论智能合约漏洞之前,我们先简单概述一下以太坊智能合约架构。

区块链网络可以部署和自动化编程脚本任务。 这些称为智能合约的程序定义了在交易期间调用的自定义函数和规则。 基于智能合约的区块链技术已经应用于金融、供应链管理、医疗能源、政府服务等各个行业。 只有特定的区块链平台支持智能合约:以太坊是第一个支持智能合约的。 EOS、Lisk、比特币和 Hyperledger Fabric 等其他区块链平台兼容部署和执行智能合约。 一种称为 Solidity 的脚本类型语言用于在以太坊平台中开发智能合约。 在这一部分中,我们介绍了与在以太坊平台上实现智能合约相关的安全漏洞。 智能合约可以持有和管理相应的功能 Credit(地址){可能价值数千美元的大量虚拟货币。 结果如何在以太坊上部署智能合约,对手不断地试图操纵智能合约的执行以支持他们的活动。 从本质上讲,智能合约运行在一个分布式的、无需许可的网络上,该网络继承了许多漏洞。 在传统系统中,可以重新开发或修补这一小组集中式应用程序。

相反,在去中心化的区块链网络中,除非采取极端措施,否则无法在现网中修改或升级已部署的智能合约。 智能合约的一个不变特征是它们的安全优势和劣势。 由于这种不变性,黑客无法为自己的利益更改或修改合约。 但是,开发者也不能在部署后修改智能合约应用。 他们可以取消或终止合约并创建一个新的智能合约,然后再次部署它。

一种。 应用层:以太坊客户端在EVM中执行智能合约,智能合约与以太坊账户相关联。 以太坊支持两种类型的账户:外部拥有账户(EOA)和合约账户。 EOA用于将用户资金存入Wei,是Ether的最小子面额,价值10-18个Ether。 EOA 与公钥关联并由公钥解析; 通过揭示相应私钥的所有权来验证对 EOA 的访问。 相反,合约账户与一段可执行字节码(即智能合约)相关联,该字节码定义了一些有趣的业务逻辑。 智能合约是 DApp 的基石。 DApp 通常有一个用户界面作为它的前端,一些智能合约作为它的后端。 一些 DApp 发行自己的加密货币,称为代币,用于首次代币发行 (ICO) 和交易所。 基于以太坊的代币是一种特殊的智能合约(例如 ERC-20)[20]。 智能合约在 EVM 中执行,EVM 是使用基于堆栈的架构的准图灵完备机器。 “准”一词表示执行受交易提供的气体限制。 在以太坊应用层,出现了各种漏洞,导致了很多攻击,如图中应用层图所示。

b. 数据层:包含区块链数据结构。 交易是一个 EOA(称为发送方)与另一个 EOA 或合约账户(称为接收方)之间的交互。 交易由以下各项指定:

(i) nonce,它是一个计数器,用于跟踪发送方发起的交易总数;

(ii) 接收者,指定交易的目标 EOA 或合约账户;

(iii) 价值,即从发送方转移到接收方的金额(以 Wei 为单位),如适用;

(iv) 输入,即交易目的对应的字节码或数据;

(v) gasPrice 和 gasLimit,分别指定发送方愿意支付给包含交易的区块的获胜矿工的单价和最大 gas 数量;

(vi) (v, r, s),即发送方的椭圆曲线数字签名算法(ECDSA)签名。 执行交易会更新所涉及账户的状态,从而更新区块链。

C。 共识层:保证区块链的状态一致。 在撰写本文时,以太坊创建一个区块大约需要 12-14 秒,这意味着多个矿工可以同时创建有效区块,并且可能有很多个区块。 以太坊使用 GHOST 共识协议的一个变体来选择“最重”的分支作为主链,其中“最重”的分支是以所讨论的分叉为根的子树,并且具有最高的累积块难度,同时注意过时块不在主链。 但是请注意,以太坊正在用权益证明 (PoS) 取代目前使用的工作量证明 (PoW)。

d. 网络层:管理节点或客户端的以太坊点对点(P2P)网络,以便节点始终可以从一些活动节点获取区块链的更新状态。 以太坊网络是一个结构化的 P2P 网络,其中每个节点(即客户端)存储整个区块链的副本。 对于节点发现和路由,每个节点维护一个包含 160 个桶的动态路由表,每个桶包含最多 16 个其他节点的 ID、IP 地址、UDP/TCP 端口条目。 以太坊使用 RLPx 协议来发现目标客户端,并使用 Ethereum Wire 协议来促进客户端之间交换以太坊区块链信息(例如交易、区块)。

e. 以太坊区块链环境:运行在以下四层环境中: 用户与以太坊区块链交互的Web界面; 以太坊客户数据库,用于存储区块链数据; 加密机制; 以及支持以太坊节点之间区块链通信的互联网基础设施。 我们将以太坊区块链架构与环境区分开来,因为针对以太坊区块链的攻击可能源自环境如何在以太坊上部署智能合约,而这些攻击可能在环境中而不是在以太坊中得到更好的解决。

1. 以太坊智能合约漏洞

重点关注以太坊系统各层的智能合约漏洞,如图2所示。

1.1 以太坊应用层:

重入:这个漏洞最初是从 DAO 攻击中发现的 [1]。 当外部被调用者合约在用户合约完成之前回调调用者合约中的某个函数(即某种意义上的循环调用)时,就会出现该漏洞。 这允许攻击者绕过适当的有效性检查,直到调用者合同中的以太币耗尽或交易被花费。

Delegation Call Injection:此漏洞最初是在对 Parity 钱包 [2] 的攻击中发现的。 为了促进代码重用,EVM 提供了一个操作码委托调用,将被调用方合约的字节码插入到调用方合约的字节码中。 因此,恶意的被调用者合约可以直接修改(或操纵)调用者合约的状态变量。 该漏洞是由于被调用方合约可以更新调用方合约的状态变量造成的。 声明旨在通过委托作为库调用的无状态合约完全不受此漏洞的影响。

冻结以太币:此漏洞最初是在对 Parity 钱包 [3] 的攻击中发现的。 该错误导致用户无法将钱存入他们的合约账户,也无法从这些账户中支出资金,从而有效地冻结了他们的资金。

升级合约:引入合约升级的思想是为了缓解智能合约一旦部署就无法修改的问题,即使后来发现它们存在漏洞。 为了允许合约升级,有两种方法:(i)将合约拆分为代理合约和逻辑合约,以便开发者可以升级后者但不能升级前者; (ii) 使用注册合约来保存更新的合约。 这些方法虽然有效,但引入了一个新的漏洞:当合约开发者变得恶意时,更新的合约可能是恶意的。 此漏洞(即不安全的联系人更新)仍然是一个悬而未决的问题。

DoS with Unexpected Revert:发生这种情况是因为调用合约遇到外部调用失败导致交易被还原,或者被调用合约故意执行恢复操作以中断调用合约的执行。 该漏洞是由执行被调用者合约恢复的调用者合约引起的。 通过让接收方调用交易来“提取”发送方为接收方保留的资金,可以有效防止发送方的交易被还原,从而防止此漏洞。

整数上溢和下溢:该漏洞最初是在对 BEC 令牌 [4] 的攻击中发现的。 当操作的结果超出 Solidity 数据类型的范围时,例如导致对攻击者的余额或其他状态变量的未授权操作,就会发生这种情况。 该漏洞是由于 Solidity 源代码未对数字输入执行正确验证造成的,并且 Solidity 编译器和 EVM 均未提供整数溢出/下溢检测。 可以通过使用 SafeMath 库来处理这些问题来防止此漏洞。

操纵余额:当合约的控制流决策取决于此值时,就会出现此漏洞。 余额,或余额,攻击者可以利用它使自己成为唯一可以获得钱的人。 可以通过在任何条件语句中不使用合约余额来防止此漏洞 [5]。 通过 tx.origin 进行身份验证:tx.origin 是 Solidity 中的一个全局变量,它指的是发起相关交易的原始 EOA。 当合约使用 tx.origin 进行授权时会出现此漏洞,可被钓鱼攻击破坏。 可以通过使用 msg.sender 而不是 tx.origin 进行身份验证来防止此漏洞,因为 msg.sender 返回导致消息的帐户。

不正确的可见性:错误地指定特征的可见性,允许未经授权的访问。

Unprotected Suicide:合约的所有者(或委托的第三方)可以使用自杀或自毁的方式来销毁合约。 当合约被取消时,其关联的字节码和存储将被删除。 该漏洞是由于合约强制执行的身份验证不充分造成的。 例如,可以通过强制执行多因素身份验证来缓解此漏洞,这意味着自杀操作必须得到多方的批准。

将以太币泄漏到任意地址:当合约的资金可以被任何调用者提取时,该漏洞就不会发生,调用者既不是合约的所有者,也不是将资金存入合约的投资者。 此漏洞是由于在调用将以太币发送到任意地址的函数时无法检查调用者的身份造成的。 可以通过正确验证发送资金的功能来防止此漏洞。

失去机密性:在区块链中,限制变量或函数的可见性并不能确保变量或函数的机密性,因为区块链的公共性质(即交易细节已知)。 防止此漏洞的一种可能解决方案是使用加密技术,例如定时承诺 [6]。

签名信息不充分:当一个数字签名对多次交易有效时会出现该漏洞,当一个发送方(如Alice)通过代理合约向多个接收方汇款(而不是发起多次交易)时,可能会出现该漏洞。 该漏洞最初是在针对智能合约的重放攻击中被利用的。 可以通过在每条消息中包含适当的信息(例如随机数和时间戳)来防止此漏洞。

具有不受限制操作的 DoS:此漏洞首先是从 Govern Mental 合同 [7] 中观察到的。

未经检查的调用返回值:此漏洞也称为错误处理异常。 它有两种变体,称为 gas-less 发送和 unchecked 发送。 当未检查低级调用的返回值时会发生这种情况,即使函数调用抛出错误 [8] 也可能继续执行。

未初始化的存储指针:回想一下,在 Solidity 中,合约状态变量总是从 slot 0 开始连续放置在存储中。对于复合局部变量(例如,struct、array 或 mapping),将对对象存储中未占用的 slot 的引用分配给指向状态变量。

错误的构造函数名称:此漏洞最初是从 Rubixi 合约 [9] 中观察到的,其中构造函数被错误命名,这使得任何人都可以成为合约的所有者。 在 Solidity 版本 0.4.22 之前,声明为与合约同名的函数被视为合约构造函数,仅在创建合约时执行。

类型转换:这个漏洞最早是在[10]中发现的。 用 Solidity 语言编写的合约可以通过直接引用被调用方合约的实例来调用另一个合约。

过时的编译器版本:当合约使用过时的编译器时发生,该编译器包含错误,从而使编译后的合约容易受到攻击。 使用最新的编译器可以防止此漏洞。

短地址:此漏洞首次在 [11] 中实现并被广泛讨论。

ETH lost to orphan address:ETH lost to orphan 汇款时发生,以太坊只检查收款地址长度不超过160位,不检查收款地址的有效性。 如果钱被发送到一个不存在的孤立地址,以太坊将自动注册该地址而不是终止交易。 由于该地址不与任何 EOA 或合约账户相关联,因此没有人可以提取转移的资金,这些资金实际上已经丢失了。 该漏洞是由于 EVM 无法进行隔离保护造成的。 在撰写本文时,只能通过手动确保收件人地址的正确性来防止此漏洞。

Call Stack Depth Limit:该漏洞由EVM执行模型不完善造成,EIP-150硬分叉修复,重新定义外部调用gas消耗规则,调用栈深度无法达到1024。

低估操作码:这个漏洞最初是从两次 DoS 攻击中发现的 [12][13]。

交易顺序依赖(aka front-running):这是指区块链即将到来的状态取决于交易执行顺序的并发问题,而是由矿工决定的。

时间依赖性:当合约使用 block.timestamp 作为触发条件的一部分或作为恶意矿工在执行关键操作(例如汇款)时可以操纵的随机源时,就会出现此漏洞。 该漏洞是由以太坊引起的,它只需要一个大于其父块的时间戳,并且在当前时钟的 900 秒内。

生成随机性:例如,许多赌博和彩票合约随机选择获胜者,一种常见的做法是根据一些初始私有种子(例如 block.number、block.timestamp、block.difficulty 或 blockhash)生成伪随机数. 然而,这些种子完全由矿工控制,这意味着恶意矿工可以操纵这些变量使自己成为赢家。 此漏洞是由受操纵的熵源引起的。

2. 数据层漏洞

不可区分的链:当以太坊分裂成两条链,ETH 和 ETC [13] 时,从跨链重放攻击中首次观察到此漏洞。 回想一下,以太坊使用 ECDSA 来签署交易。 在 EIP-155 [7] 硬分叉之前,每笔交易包含六个字段(即随机数、收件人、价值、输入、gasPrice 和 gasLimit)。 但是,数字签名不是特定于链的,因为此时甚至不知道特定于链的信息。 因此,为一条链创建的交易可以被另一条链重复使用。 通过将 chainID 合并到字段中,该漏洞已被消除。

State Trie 中的“Empty Account”:该漏洞最初是在参考文献 [12] [13] 中报告的 DoS 攻击中发现的。

3. 共识层的漏洞

可外包的难题:回想一下,以太坊采用了一个名为 Ethash 的 PoW 难题,它被设计为抗 ASIC 并能够限制并行计算的使用(由于矿工的大部分工作将读取数据集。内存带宽)。 然而,狡猾的矿工仍然可以将寻找谜题解决方案的任务分成多个更小的任务,然后外包出去。 该漏洞是由Ethash引起的,它只在原始图像搜索中使拼图部分顺序,而不是依赖于顺序PoW。

概率最终性:此漏洞是由以太坊区块链的设计造成的,该设计有利于可用性而不是一致性,这是根据 CAP 定理 [14] 选择的。

带有块填充的 DoS:此漏洞首先是从 Fomo3D 合约 [15] 中观察到的。 该漏洞仅导致攻击者的交易被包含在新挖出的区块中,而其他交易则在一段时间内被矿工遗弃。 当攻击者提供更高的 gasPrice 以激励矿工选择攻击者的交易时,就会发生这种情况。 该漏洞是由贪婪的挖矿激励机制引起的。 在撰写本文时,没有解决方案可以防止此漏洞。

Honest Mining Assumption:此漏洞是由共识协议引起的,因为它与激励不兼容,请参见[16]。 在撰写本文时,此漏洞仍是一个悬而未决的问题。

Block Reward:指的是区块奖励机制,用于应对由于区块生成速度过快导致旧区块增加的情况。 然而,这种机制有一个副作用,即允许自私矿工将陈旧区块变成区块并获得奖励,有效地激励自私挖矿和双花。

Validator's Dilemma:此漏洞首先在参考文献中报告。 [17],指的是当验证新交易需要毫不费力的计算时,无论矿工是否选择验证交易,都容易受到攻击。 如果矿工验证计算密集型交易,他们会花费大量时间并让攻击者在争夺下一个区块时占据优势; 如果矿工未经验证就接受交易,区块链可能包含不正确的交易。 该漏洞是由于在以太坊中验证资源需求交易的高成本造成的。 可以通过限制验证块中所有交易所需的计算量来缓解此漏洞 [17]。 但是,尚不清楚如何消除此漏洞。

4. 网络层的漏洞

4.1 创建无限节点:该漏洞针对1.8之前的Geth客户端版本。 在以太坊网络中,每个节点都由一个唯一的 ID 标识,该 ID 是一个 64 字节的 ECDSA 公钥。 攻击者可以在一台计算机上创建无限数量的节点(即具有相同的 IP 地址),然后使用这些节点来独占某些受害者节点的传入和传出连接,有效地将受害者与网络中的其他节点捆绑在一起。 其他对等节点是隔离的。 该漏洞是由于对节点生成过程的弱限制造成的。 可以通过使用 IP 地址和公钥的组合作为节点 ID 来消除此漏洞。 Geth 开发人员尚未实施此对策,他们认为这会对客户端的可用性产生负面影响。

4.2 不受限制的传入连接:此漏洞存在于版本 1.8 [18] 之前的 Geth 客户端中。 每个节点在任何时间点都可以拥有总共 maxpeers 连接(默认为 25),并且最多可以与其他节点发起 1(1+maxpeers)/2? 出站 TCP 连接。 但是,其他节点发起的传入 TCP 连接数没有上限。 通过为没有出站连接的受害者节点建立许多 maxpeers 的传入连接,攻击者有机会使受害者黯然失色。 在 Geth v1.8 中,通过对一个节点的传入 TCP 连接数施加上限,默认值为 ? 最大点数/3? = 8,消除了这个漏洞。

4.3 公共节点选择:在 1.8 [18] 之前的 Geth 客户端版本中检测到此漏洞。

4.4 Peer Selection:该漏洞是指Geth客户端在从路由表中选择节点建立出站连接时,总是得到一个随机选择的bucket的head。 由于每个桶中的节点都是按活动排序的,因此攻击者可以通过定期向 Geth 客户端发送消息来使他们的节点始终领先于其他节点。 通过在路由表中的所有节点集合中随机选择一个统一节点,而不是仅仅从每个桶的标头中随机选择节点,Geth v1.9 中已消除此漏洞。

4.5 独立块同步:它允许攻击者在不垄断受害客户端连接的情况下分割以太坊 P2P 网络。 回想一下,每个区块头都包含一个难度字段,它记录了该区块的挖矿难度。 区块链的总难度用 totalDifficulty 表示,它是到当前区块为止的难度总和。

4.6 RPC API 暴露:该漏洞最初是通过攻击 Geth 和 Parity 客户端 [19] 发现的。