本文总结了常见智能合约的安全漏洞及预防措施,并对项目方和一般用户给出了一些安全建议
作者:Max Cobo 安全总监
封面:Photo by Armand Khoury on Unsplash
此篇是 Cobo Global 的第 24 篇文章
受 Moledao 邀请,Cobo 安全总监 Max 近日通过网络为社区成员分享了一堂 DeFi 安全课。Max 为大家回顾了过去一年多 Web3 行业遭遇的重大安全事件,并着重探讨了这些安全事件发生的原因以及如何规避,总结了常见智能合约的安全漏洞及预防措施,还对项目方和一般用户给出了一些安全建议。在此,我们将 Max 分享的内容分为两篇发布,供 DeFi 爱好者收藏。
上篇:《Cobo DeFi 安全课(上):复盘 2022 DeFi 安全大事件》
常见的 DeFi 漏洞类型一般有闪电贷、价格操纵、函数权限问题、任意外部调用、fallback 函数问题、业务逻辑漏洞、私钥泄漏、重入。
2022 年 11-12 月安全事件统计
重点介绍一下闪电贷、价格操控以及重入攻击这三种类型。
闪电贷
- 常⻅攻击往往伴随着闪电贷,攻击者喜欢通过闪电贷借出大量资金,对价格进行操纵,攻击业务逻辑等等;
- 开发者需要考虑,合约功能是否会因为巨额的资金导致功能异常,或通过巨额资金在一笔交易中与合约中多个函数交互获取奖励这些场景;
- 经常可以看到合约中一些 Token 数量的值被用于计算奖励,或是使用 DEX 交易对中的 Token 数量参与各种计算,由于开发者在设计这些功能时没有考虑到攻击者可以利用闪电贷操控这些变量,导致合约资金被盗。
闪电贷本身是 DeFi 的一种创新,但是当被黑客利用时,他们不需要花费任何成本就可以借钱,在执行完整个套利流程后再归还,剩下的部分就是套利的收益,他们不需要付出任何代价,只需要支付少量的 Gas 费用就可以获得巨额收益。
在过去的两年里,闪电贷出现了很多问题。许多 DeFi 项目看起来收益很高,但实际上项目方的水平参差不齐。比如代码有可能是买来的,即便代码本身没有漏洞,在逻辑上仍然可能存在问题。例如,我们曾经遇到过一个项目,会在固定的时间根据持仓者持有的代币数量发放奖励,却被攻击者利用闪电贷购买大量代币,在奖励发放时,大部分奖励都流向了攻击者。此外,还有一些通过 Token 来计算价格的项目,可以通过闪电贷影响价格,作为项目方应该对这些问题提高警惕。
价格操控
价格操控问题与闪电贷关系密切,这个问题主要是由于价格计算时其中一些参数可以被用户控制,经常出现的问题的类型有两种。
- 一种是计算价格时使用第三方的数据,但使用方式不正确或检查缺失导致价格被恶意操控。
- 另外一种是由于使用了一些地址的 Token 数量作为计算变量,而这些地址的 Token 余额可以被临时增加或减少。
重入攻击
调用外部合约的主要危险之一是它们可以接管控制流,并对你的数据进行调用函数未预料到的更改。
“`
mapping (address => uint) private userBalances; function withdrawBalance() public {
uint amountToWithdraw = userBalances[msg.sender];
(bool success, ) = msg.sender.call.value(amountToWithdraw)(“”); // 如果 msg.sender 是一个合约,那么合约的 fallback 代码会被执行,并可以再次调用 withdrawBalance 完成重入攻击
require(success);
userBalances[msg.sender] = 0;
}
“`
由于用户的余额直到函数的最后才设置为 0,第二次(和以后的)调用仍然会成功,并且会一遍又一遍地提取余额。
针对于不同的合约,重入存在的方式很多,可以结合合约的不同函数或多个不同合约的函数,完成重入攻击,所以我们在解决重入问题时需要注意以下几点:1. 不只是防止单一函数的重入问题;2. 遵循 Checks-Effects-Interactions 模式进行编码 ;3. 使用经过时间验证的防重入 modifier。
其实最怕的就是重复造轮子,需要什么都自己写,在这个圈内有很多最佳安全实践,我们拿来用就好,完全没有必要重复造轮子。当你造一个轮子的时候,是没有通过充分的验证的,这个时候出问题的概率,很明显比用一个非常成熟的久经考验的出问题的概率要大得多。
Omni Protocol 由于重入问题被黑客攻击,它还有另外一个故事:
Omni Protocol 漏洞背后的故事 — 四个黑客之间的较量
以太坊的 mempool 一直被大量黑客监视,他们不断的分析交易并对有利可图的交易进行抢跑,来获取利润。Omni Protocol 漏洞发现者提交的攻击交易,就被两位黑客捕捉到,他们利用抢跑机器人在 flashbot 发布抢跑交易夺取了 Omni Protocol 协议中的 1200 ETH,而发现漏洞的攻击者却只获得了 480ETH。在此期间,还有一位黑客发现了抢跑黑客在 flashbot 提交的攻击交易,利用攻击交易需要购买 Doodle ERC20 代币的特点,对其进行了三明治攻击,获利 151ETH。
发现漏洞的人获利并不是最多的,获利更多的是黑暗森林里其他的猎人。在这个生态中遍地都是猎人,猎人之间可能互为猎物,哪怕是攻击的发起者,如果是新手的话,可能也拿不走这个项目的大部分钱,除非一笔拿走。还有很多人会用更高的 Gas 费抢先把交易执行,在抢跑的过程中如果涉及到了在 DEX 中的代币的买卖的话,会涉及到被三明治攻击,非常的混乱。
安全建议
最后是给项目方和普通参与用户的安全建议。
项目方安全 Tips
一、合约开发遵循最佳安全实践。
二、合约可升级、暂停:很多攻击不是一次性的把币全转走,而是分多笔交易去执行,如果有一个相对健全的监控机制的话,是可以发现的,发现之后合约假设是可以暂停的,就可以有效的去降低损失。
三、采用时间锁:像 Ankr 的案例,如果有时间锁的话,假设是 48 小时之内才能完成,这个时候在时间锁的很多人能发现这个创建者重新更新了一个 mint 的方法,且谁都可以用,监控的人就知道项目应该是被黑了,就可以通知项目方去改变,即使假设通知了也没人管,至少可以先把自己的那部分钱给拿出来,先保证自己的收益不受损。所以说一个项目如果没有时间锁的话,理论上来讲是增加了出问题的概率。
四、加大安全投入,建立一套完善的安全体系:安全不是一个点,也不是一条线,安全是成体系的。不要觉得作为项目方,合约通过多家公司审计就没问题了,结果朝鲜黑客把私钥盗走了,哪怕是多签,把几个私钥全部都盗走了。或者是说经济模型有问题,业务模式有问题……能导致钱丢的方式有千千万万种,就看在事前能不能将安全风险全都规避掉。要尽量做风险建模,然后逐步地将大部分风险规避掉,剩余风险也是可接受风险,在可承受范围内。安全和效率是不可能兼得的,要做一定的取舍。但如果完全不管安全,在安全上没有投入的话,被攻击是很正常的。
五、提高所有员工的安全意识:提高安全意识并不需要很多技术。在 Twitter 上我们经常能看到很多人被钓鱼丢失 NFT,其实钓鱼的方式就是利用了人性的弱点,可能多注意一点,就不会中招。在 Web3 这个大环境内,只要稍微多问一些为什么,稍微多想一点就能规避掉很多问题。
六、预防内部作恶,在提升效率的同时增强⻛控:再拿 Ankr 来举例。第一,合约的 Owner 是一个单签而不是多签,私钥丢了整个项目就被控制了;第二,没有用时间锁,一些关键性的操作更新,立马就更新了,没有人知道,对于整个协议的参与者是非常不公平的;再就是内部作恶,内部的安全机制没有起到任何作用。
链上的协议如何在保证效率的同时,在安全性上也有一定提升?这里做一个广告,推荐 Cobo Safe,假设一个项目采用多签,我们操作的时候,稍微会影响一下效率。通过 Cobo Safe 可以指定某个人执行某个操作,比如是五分之三多签;可以指定某一个地址可以做某一个操作,比如可以给到一个非常值得信赖的一个节点做安全风险监控,假设发现到协议正在被攻击,资金正在逐渐往黑客地址转的时候,如果这个合约本身有暂停的功能,那我们将暂停功能授予给了某个人,他就可以去做这个操作。
再比如说给 DEX 做市的做市商,如果不去限制 Owner 权限的话,那 Owner 可以把钱给转到别的地址,通过 Cobo Safe 可以限制转币的地址,限制只能操作哪几个币对,设置只能把钱提到某个地址。Cobo Safe 在效率提升的同时,保证了一定的安全性。
七、三方引入安全性:作为生态中的一环,项目方都会有自己的上下游。安全上有一个 “默认上下游都是不安全” 的原则。无论对于上游还是下游,都要做校验。对于三方我们是很难控制,安全的风险敞口实际上特别大,所以要很注意三方的引入。合约是开源的,可以去引入、调用它;如果合约不开源,就绝对不能引用。因为我们不知道里面的逻辑是什么,或者是引入这个合约本身就是一个可升级的合约,正常用可能没问题,但我们无法预知是不是突然有一天会升级变成一个作恶的合约,这是无法控制的。
用户/LP 如何判断智能合约是否安全?
对于普通用户,我们判断项目是否安全主要看以下六点:
一、合约是否开源:凡是合约不开源的项目,坚决不碰,因为我们无从得知合约写的是什么。
二、Owner 是否采用多签,多签是否去中心化:如果不用多签,我们无法判断项目的业务逻辑和内容,一旦出现安全事件,无法判断是否为黑客所为。即便采用多签,也需要判断一下多签是不是去中心化的。
三、合约已有的交易情况:尤其市面上有很多搞钓鱼诈骗的项目,可能会做一个比较相似的合约,这个时候我们就要看一下合约的部署时间、交互次数等,这些都是判断合约是否是安全的衡量标准。
四、合约是否为代理合约,是否可升级,是否有时间锁:如果合约完全不能升级,就太死板了,还是推荐项目的合约是可以升级的。但是升级要讲究方法,当升级有升级内容、重要参数更改的时候,要有一个时间锁定,要有给大家一个时间窗口去判断真实升级是对用户有害的还是有利的,这也是公开透明的一种方式。
五、合约是否接受过多家机构审计(不要盲目信任审计公司), Owner 权限是否过大:首先不要只相信一家审计公司,因为不同的审计公司,不同的审计人员,看问题的角度是不一样的。如果一个项目有多家机构的审计结果,发现了不同的问题,项目方都做了修复的话,相比较只有某一家审计公司审计要更安全。一个负责任的项目方会找多家审计机构做交叉的审计。
其次要看 Owner 的权限是否过大。比如说有一些貔恘盘项目,Owner 可以完全控制用户的资金,如果代币买的少可以正常买卖,如果代币购买量巨大,Owner 立马可以控制去锁死,而无法交易。还有一些 NFT 项目也是一样的。一个正常的项目 Owner 的权限一定是可控的,这样就不会有太多高危操作,操作也会用时间锁的方式,让用户知道操作的是什么。尤其是行情不好的时候,有各种各样的跑路盘,所以大家对于 Ownner 的权限一定要重视。
六、注意预言机:如果项目使用市面上的龙头预言机,基本上不会有太大问题,但如果使用自建的预言机,或者用一些随便抵押一些代币就可以往里去喂价的预言机,那就要注意了。当发现预言机可能会存在一些问题,或者说存在被操纵可能的时候,即便项目收益再高也不能参与。