这张图总结了智能合约攻防的各个方面,分为两大部分:链上攻防和链下攻防。
链上攻防
对于链上攻防,选取5个方面进行详细剖析。“Talk is cheap. Show me the code.” 下面开始展示源码详细解读。
1、溢出
因为没有使用 SaceMath导致的溢出造成金额可以在攻击者构造的数据中被任意控制,从而导致条件判断成立最终攻击者成功攻击了该智能合约。
此类问题避免方法:在做数字计算的相关代码处严格使用 SafeMath 进行做算术运算,防止溢出产生。
2、权限控制
此类权限控制属于权限问题中的一种,由于合约开发人员的不专业导致的代理转账函数中未进行授权判断,从而导致任何人都可以转走别人的钱。
此类问题避免方法:找专业的开发人员进行合约开发,同时合约开发人员也需要及时提高自己的开发技能以及专业能力,由于区块链的去中心和Token的特殊性很容易因为一个小的漏洞导致项目方从此身败名裂,所以建议项目上线之前找专业的安全审计团队出职业的审计报告,此类问题慢雾在审计的过程中一定能够发现并帮助项目方避免这种尴尬的情况。
3、条件竞争
此类问题为条件竞争导致的跨合约调用多次恶意转账最终攻击者成功完成攻击,在跨合约调用方面一直存在很大的问题,由于开发者的专业技能不够专业或者是合约设计的不合理等问题都会导致此类条件竞争。
此类问题避免方法:了解 Solidity 语言本身的函数使用场景以及底层实现,并且在业务逻辑上先扣除需要减去的账户金额再添加到对应账户并及时清零中间变量的临时值,避免因为事务的问题导致多入账导致损失,同时也要了解清楚Solidity中someAddress.call.value()方法 和 someAddress.transfer() 方法还有someAddress.send() 方法的区别。非常不建议使用 someAddress.call.value(),具体原因请大家一定要自己动手去查一下。
4、假充值
假充值这类问题是一个系列,比如XRP的假充值和USDT的假充值等,在这里只讲 ERC20 的假充值问题,这种问题是由于合约开发人员的逻辑判断代码不规范导致在以太坊的区块浏览器中可以看到原本一笔失败的转账状态为Success交易所在进行充值入账的时候也没有严格进行状态的判断和金额的校验从而导致了此类问题,此类问题杀伤力强,并且一次足以让一个交易所亏损上百万及千万,无论是开发者还是交易所都需要足够重视假充值的问题。
此类问题避免方法:合约开发中代码判断逻辑的地方使用 require()或者 assert()进行判断,如果条件不满足会直接导致 transfer 的失败同时状态也是 fail ,交易所钱包业务开发人员需要在充值的时候注意严格校验转账状态是成功还是失败,同时对充值金额也进行校验,确认真的到账后此笔交易才算成功。
5、恶意事件
此处需要说明这里的代码仅仅是我们编写作为演示使用的,目前暂时没有发现这种真是的攻击行为。由于区块链的数据都记录在链上,恶意记录 event 的事件可以直接修改对应的参数,如果此代码真是存在则上面讲的假充值就真的成了真充值了。
此类问题避免方法:做好 Code Review 和找专业的代码审计。
链下攻防
对于链下攻防,同样从5个方面详细剖析。
1、WEB
在之前发生的MyEtherWallet发生的域名劫持事件前,实际上相关的安全机构已经给出了中级风险的提示(如下图),但是它并没有对此重视。
专题介绍:https://mp.weixin.qq.com/s/-Yiul1QtSNa9JJAOuXCx3A
2、终端
对于硬件钱包的安全,也可能存在各种的安全隐患如:是否使用加密芯片,工业设计是否安全,做工是否很简陋不考虑丢失以及意外破损和能否以应对低温高温的情况下正常使用,最终需要考虑你到底是买了个硬件还是真正的硬件钱包?
3、节点
比如以太坊黑色情人节事件,攻击者通过在网络上通过 P2P协议发现新的以太坊全节点,然后构造好攻击脚本做好工程化等待时机对新搭建的全节点进行攻击,由于很多小白用户不懂得如何防御此类攻击所以到目前位置还是有很多团队不断被盗ETH。
详见慢雾的专题页:https://4294967296.io/eth214/
专题介绍:https://mp.weixin.qq.com/s/-Yiul1QtSNa9JJAOuXCx3A
慢雾的防御方法:https://mp.weixin.qq.com/s/Kk2lsoQ1679Gda56Ec-zJg
4、矿工
矿工有可能作恶,针对Dapp,进行选择性的打包。针对矿池,针对块代扣攻击等。
5、后端
后端的攻击举例为,USDT 假充值。
攻击步骤为:
1)向交易所钱包构造并发起⽆效(虚假)转账交易;
2)由于逻辑判断缺陷交易所将⽆效交易⼊账并计⼊到⽤户在交易所的资⾦账户;
3)⽤户发起提币;
4)交易所处理⽤户提币将币打到⽤户⾃⼰钱包地址;⽤户⾃⼰充币环节USDT 没有任何损失,提币环节交易所把⾃⼰真实的 USDT 币打给⽤户,造成交易所损失。
这背后的原理是:
USDT 是基于 Bitcoin 区块的 OMNI 协议资产类型,利⽤ Bitcoin 的 OP_RETURN 承载相关交易数据;
Bitcoin 本⾝并不会校验 OP_RETURN 数据的“合法性”,可以是任意数据;
Bitcoin 交易当区块确认数达到 6 的时候,就会被Bitcoin 节点承认;
那么,USDT 的交易在 OMNI 的节点上如何被确认的呢?
int64_t nBalance =
getMPbalance(sender,property, BALANCE);
说明:通过上⾯的代码来看,OMNI内部有⾃⼰的⼀套基于地址的记账模型,通过地址可以获取地址的余额。
因此,为了避免以上问题。⼀笔 USDT 的交易合法的,要⾄少满⾜以下两个条件:
(1)要通过⽐特币的交易来构造,要符合⽐特币的余额验证(BTC)及交易规则验证
(2)要通过 USDT ⾃⼰的余额(USDT)验证
对于USDT 假充值,当余额不足时会发生什么?
{"amount": "28.59995822",
"block": 502358,
"blockhash": "0000000000000000005968fa48c49d7c4fb2363369d59db82897853fd937c71a",
"blocktime": 1514985094,
"confirmations": 37854,
"divisible": true,
"fee": "0.00200000",
"flags": null,
"invalidreason": "Sender has insufficient balance",
"ismine": false,
"positioninblock": 301,
"propertyid": 31,
"propertyname": "TetherUS",
"referenceaddress": "1Po1oWkD2LmodfkBYiAktwh76vkF93LKnh",
"sendingaddress": "18DmsHjHU6YM2ckFzub4pBneD8QXCXRTLR",
"txid": "1b5c80f487d2bf8b69e1bbba2b1979aacb1aca7a094c00bcb9abd85f9af738ea",
"type": "Simple Send",
"type_int": 0,
"valid": false,
"version": 0 }
另外,有些黑客非常有心机,在一些百度回答等贴上错误的代码,如果程序员不仔细检查或者对智能合约不熟悉,直接复制粘贴代码,则容易遭受攻击。要如何提升安全呢?首先开发人员拥有安全开发意识,其次还是要找专业的安全团队做专业的审计。