以太坊智能合约设计缺陷之条件竞争
摘要: 开发者在进行代码开发时常常倾向于认为代码会以线性的方式执行,而且他们忽视了并行服务器会并发执行多个线程,这就会导致意想不到的结果。
前言
2016年11月29号,有人在一篇文章中提到到了一个在ERC20标准中存在的隐患问题,条件竞争。
首先了解一个定义——竞争条件是什么?条件竞争漏洞是一种服务器端的漏洞,由于服务器端在处理不同用户的请求时是并发进行的,因此,如果并发处理不当或相关操作逻辑顺序设计的不合理时,将会导致此类问题的发生。
开发者在进行代码开发时常常倾向于认为代码会以线性的方式执行,而且他们忽视了并行服务器会并发执行多个线程,这就会导致意想不到的结果。
线程同步机制确保两个及以上的并发进程或线程不同时执行某些特定的程序段,也被称之为临界区(critical section),如果没有应用好同步技术则会发生“竞争条件”问题。
在很多系统中都会包含上传文件或者从远端获取文件保存在服务器的功能(如:允许用户使用网络上的图片作为自己的头像的功能),下面是一段简单的上传文件释义代码:
这段代码看似一切正常,先通过copy($GET['src'],$GET['dst'])将文件从源地址复制到目的地址,然后检查$GET['dst']的安全性,如果发现$GET['dst']不安全就马上通过unlink($_GET['dst'])将其删除。但是,当程序在服务端并发处理用户请求时问题就来了。如果在文件上传成功后但是在相关安全检查发现它是不安全文件删除它以前这个文件就被执行了那么会怎样呢?
假设攻击者上传了一个用来生成恶意shell的文件,在上传完成和安全检查完成并删除它的间隙,攻击者通过不断地发起访问请求的方法访问了该文件,该文件就会被执行,并且在服务器上生成一个恶意shell的文件。至此,该文件的任务就已全部完成,至于后面发现它是一个不安全的文件并把它删除的问题都已经不重要了,因为攻击者已经成功的在服务器中植入了一个shell文件,后续的一切就都不是问题了。
由上述过程我们可以看到这种“先将猛兽放进屋,再杀之”的处理逻辑在并发的情况下是十分危险的,极易导致条件竞争漏洞的发生。
介绍
这里举一个approve函数中会出现的比较典型的例子,approve一般用于授权,比如授权别人可以取走自己的多少代币,整个流程是这样的:
1.用户A授权用户B 100代币的额度
2.用户A觉得100代币的额度太高了,再次调用approve试图把额度改为50
3.用户B在待交易处(打包前)看到了这笔交易
4.用户B构造一笔提取100代币的交易,通过条件竞争将这笔交易打包到了修改额度之前,成功提取了100代币
5.用户B发起了第二次交易,提取50代币,用户B成功拥有了150代币
想要理解上面这个条件竞争的原理,首先我们得对以太坊的打包交易逻辑有基础认识。
简单来说就是
1.只有当交易被打包进区块时,他才是不可更改的
2.区块会优先打包gasprice更高的交易
所以当用户B在待打包处看到修改的交易时,可以通过构造更高gasprice的交易来竞争,将这笔交易打包到修改交易之前,就产生了问题。
以下代码就存在条件竞争的问题
影响范围
使用Haotian平台智能合约审计功能可以准确扫描到该类型问题。
基于Haotian平台智能合约审计功能规则,我们对全网的公开的共39548 个合约代码进行了扫描,其中共24791个合约涉及到这类问题。
截止2018年8月10日为止,我们发现了22981个存在approve条件竞争的合约代码,其中15325个合约仍处于交易状态,其中交易量最高的10个合约情况如下:
修复方式
关于这个问题的修复方式讨论很多,由于这属于底层特性的问题,所以很难在智能合约层面做解决,在代码层面,我们建议在approve函数中加入
将这个条件加入,在每次修改权限时,将额度修改为0,再将额度改为对应值。
在这种情况下,合约管理者可以通过日志或其他手段来判断是否有条件竞争发生,从风控的角度警醒合约管理者注意该问题的发生。范例代码如下:
总结
条件竞争是个比较特殊的问题,这里的条件竞争涉及到了智能合约底层实现逻辑,本身打包逻辑存在条件竞争,我们无法在代码层面避免这个问题,但对于开发者来说,比起无缘无故的因为该问题丢失代币来说,更重要的是合约管理者可以监控到每一笔交易的结果,所以我们加入置0的操作来提醒合约管理者、代币持有者该问题,尽量避免这样的操作发生。这里 曲速未来 再次建议所有的开发者重新审视自己的合约代码,检查是否存在设计缺陷问题,避免不必要的麻烦以及安全问题。
(作者:区块链安全档案,内容来自链得得内容开放平台“得得号”;本文仅代表作者观点,不代表链得得官方立场)
评论(0)
Oh! no
您是否确认要删除该条评论吗?