区块链合约安全系列(二):如何认识及预防公链合约中的重入攻击漏洞

id:BSN_2021
公众号:BSN研习社

背景:由于公链环境下所有的信息都是共享的,智能合约相当于是完全透明化,任何人都可以调用,外加一些利益的驱动,导致引发了很多hacker的攻击。其中重入 (Re-Entrance) 攻击是以太坊中的攻击方式之一,例如The DAO 事件被盗取了大量的以太币从而造成了以太坊的硬分叉。

目标:将目标合约里的所有资金(ether)进行盗取,从而认识以及预防重入攻击漏洞。

对象:适用于用Solidity语言开发的智能合约,例如BSN中的武汉链(基于ETH)和泰安链(基于 fisco bcos)上运行的智能合约。

前言

在进入今天的正题之前,我们先来看一段典型的有重入漏洞的代码:
区块链合约安全系列(二):如何认识及预防公链合约中的重入攻击漏洞_第1张图片
上述方法的业务也很简单,提取调用者存入的钱,成功后将其余额设为零。

不知道大家脑海里有没有发出这样的疑问:这怎么就存在重入漏洞了呢?

如果你存在这样的疑惑,那么请由我来讲解一下其中的知识。在开始之前需要大家清楚几个知识点:

1.重入定义
什么是重入?官方的解释如下:
区块链合约安全系列(二):如何认识及预防公链合约中的重入攻击漏洞_第2张图片
为方便记忆,暂且可以简单的理解为我们开发者传统印象中的递归。

2.特殊函数
这里的特殊函数是指在合约交互发送ether转账时,会隐式触发调用的函数,包含receive和fallback。官方的解释如下:
区块链合约安全系列(二):如何认识及预防公链合约中的重入攻击漏洞_第3张图片
通俗的来说就是当你的合约接收ether时,必须至少包含这俩函数中的一个。

3.转账操作

什么是转账???对,没错,就是你脑子里想的那样。在这里是指在两个以太坊账户之间的发送和接收ether的操作。但需要知道的一点是以太坊上的账户分为两种,即普通账户和合约账户。

在以太坊上转账操作常见的函数包括三种.transfer()、.send()和.call{value:xxx}("")

在各位了解了基本的知识后,下面我们开始进入今天的正题:我将通过一个示例来进行演示,通过重入漏洞,盗取一个“银行”里的全部资金。

示例涉及到两个合约,一个为资金管理合约,一个为攻击者合约。其完整代码如下:
区块链合约安全系列(二):如何认识及预防公链合约中的重入攻击漏洞_第4张图片

合约部署

为其方便演示在此使用remix进行测试环境准备。首先,部署资金管理合约,并初始化存入100Wei(为显示方便,其金额任意)。结果如下:
区块链合约安全系列(二):如何认识及预防公链合约中的重入攻击漏洞_第5张图片

然后,部署攻击者合约,结果如下:
区块链合约安全系列(二):如何认识及预防公链合约中的重入攻击漏洞_第6张图片

重入攻击

首先,存入ether。因为资金管理合约的提款逻辑为提取自己已存入的,所以我们需要先存入。结果如下图:
区块链合约安全系列(二):如何认识及预防公链合约中的重入攻击漏洞_第7张图片

然后,提取ether,利用重入漏洞将额外的资金进行盗取。交易执行成功后,查询提取的资金信息,结果如下图:
区块链合约安全系列(二):如何认识及预防公链合约中的重入攻击漏洞_第8张图片

该笔交易的事件日志信息如下:
区块链合约安全系列(二):如何认识及预防公链合约中的重入攻击漏洞_第9张图片

另外,我们查看一下资金管理合约的余额信息,查询结果如下图:
区块链合约安全系列(二):如何认识及预防公链合约中的重入攻击漏洞_第10张图片

解决方案

通过上面的示例,细心的朋友可能已经大概明白,其实重入攻击漏洞是因为触发了特殊函数(receive或者fallback)造成递归调用转账,目前常用的解决方案有下面几种:

方案一、 使用安全的转账函数
使用内置的 transfer或send 函数,因为transfer和send在转账时会传递2300Gas,不足以执行重入调回合约操作,而.call会传递所有可用的Gas(当然也可以设置传递可用的Gas)。

方案二、使用官方推荐的检查-生效-交互模式 (checks-effects-interactions)
区块链合约安全系列(二):如何认识及预防公链合约中的重入攻击漏洞_第11张图片
方案三、使用专门针对重入攻击的安全合约

例如OpenZeppelin 官方库中的安全合约ReentrancyGuard.sol
区块链合约安全系列(二):如何认识及预防公链合约中的重入攻击漏洞_第12张图片
今天的讲解到此结束,感谢大家的阅读,如果你有其他的方案,欢迎一块交流。

你可能感兴趣的:(区块链)