以太坊智能合约编程的典型漏洞汇集
PeckShield网站近期公布了一些以太坊智能合约漏洞分析,包括
batchOverflow[1], proxyOverflow[2], transferFlaw[3],ownerAnyone[4], multiOverflow[5],
看了一下,感觉以太坊智能合约编程容易出问题的地方还是蛮多的,这些问题都是由于以太坊智能合约本身的特殊性决定的(控制资金流转,功能函数直接面向用户,合约部署后无法做升级修护等等)。
这里先记录一点整理,后续陆续记录、分析;
编程语言语法、语义要熟悉
估计有不少人,比如我,solidity编程就是稍微看了点例子,就直接动手编写代码的,这样就很可能会隐含了一些意想不到的错误,所以应该要尽量熟悉solidity语法。若没时间去抠语法细节,就要用非常简单的、没有歧义的语句来表达程序功能,未必要用编程语言本身提供的复杂的功能。不明确的、有点绕弯的功能,可以不用。
整数算术运算溢出
若不想使用SafeMath,涉及到两个无符号数相加或相乘的时候,可以先计算,然后判断其结果应该大于任何一个操作数
SUM = A + B
require(SUM >= A && SUM >= B)
MUL = A * B
require(MUL>= A && MUL >= B)
若涉及减法,可以这么写:
require(A >= B)
SUB = A - B
对于除法:
require(A >= B && B != 0)
DIV = A / B
类似的,还有乘方操作,都是按照这个思路加必要的判断语句就可以了。
合约地址与普通地址区分对待
涉及到汇款、转账操作时,一定要注意区分接收者是普通账户,还是合约账户,尽量不在一个合约里给另一个合约地址转账;
外部程序判断:
var code = web3.eth.getCode("account_address")
if(code === '0x') console.log('regular account')
else console.log('contract accout')
合约内部判断:
function isContract(address _addr) private returns (bool isContract) {
uint32 size;
assembly {
size := extcodesize(_addr)
}
return (size > 0);
}
尽量不用递归调用,仔细考虑被调与主调关系
合约编程和一般的编程不一样,更注重安全可靠,尽量不要用递归。合约代码尽量是封闭的,既不调用未知的代码,也不被未知的代码调用(通过加某些特殊判断语句)。若做不到这一点,就需要慎重考虑如果别人的未知代码调用我的代码,可能发生什么,我的代码调用了别人的未知代码会发生什么。
合约用户归类,合约函数归类,某些函数不能让任何人都能调用
若某些函数的功能比较特殊,则可以设计为只有owner可以调用,或者是owner授权的用户才能调用,不可以让任意用户都可以发交易触发调用。比如针对proxyOverflow漏洞来说,那个代理函数其实应该由合约部署者授权调用的,然而却没有加限制,导致攻击者在一笔交易中构造出特殊的参数,并触发了函数运行,通过整数溢特性出制造了巨量的Token。