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

id:BSN_2021
公众号:BSN研习社

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


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


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

前言

在进入今天的正题之前,我们先来看一段典型的有重入漏洞的代码:

上述方法的业务也很简单,提取调用者存入的钱,成功后将其余额设为零。

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

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

1.重入定义
什么是重入?官方的解释如下:

为方便记忆,暂且可以简单的理解为我们开发者传统印象中的递归。

2.特殊函数
这里的特殊函数是指在合约交互发送ether转账时,会隐式触发调用的函数,包含receive和fallback。官方的解释如下:

 

通俗的来说就是当你的合约接收ether时,必须至少包含这俩函数中的一个。

3.转账操作


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

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

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

示例涉及到两个合约,一个为资金管理合约,一个为攻击者合约。其完整代码如下:

合约部署

为其方便演示在此使用remix进行测试环境准备。首先,部署资金管理合约,并初始化存入100Wei(为显示方便,其金额任意)。结果如下:

然后,部署攻击者合约,结果如下:


重入攻击

首先,存入ether。因为资金管理合约的提款逻辑为提取自己已存入的,所以我们需要先存入。结果如下图:

然后,提取ether,利用重入漏洞将额外的资金进行盗取。交易执行成功后,查询提取的资金信息,结果如下图:

 

该笔交易的事件日志信息如下:

 

另外,我们查看一下资金管理合约的余额信息,查询结果如下图:

 

解决方案

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

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

方案二、使用官方推荐的检查-生效-交互模式 (checks-effects-interactions)

方案三、使用专门针对重入攻击的安全合约

例如OpenZeppelin 官方库中的安全合约ReentrancyGuard.sol

今天的讲解到此结束,感谢大家的阅读,如果你有其他的方案,欢迎一块交流。 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值