"
收集一众行业大咖观点,探索区块链商业及应用。百家争鸣、百花齐放,说理、解密、预测和八卦,了解行业内幕,看咖说就够了!
投稿请联系 :tougao@conflux-chain.org
本期作者:Tiny熊,深入浅出区块链社区发起人、登链科技创始人、登链学院首席讲师。
"
不要因为一次攻击,就拒绝使用新技术。
可重入攻击不是 ERC777 的错
周末两天 Uniswap 和 Lendf.me 都发生了黑客攻击事件,都是 DeFi 应用与 ERC777 组合应用导致可重入漏洞,其中导致 Lendf.me 损失抵押资产千万美元。
发生这样的事情,相信是所有从业者不愿意看到的,本文也无意针对 Lendf.me,你们也是受害者,只是看到有人甩锅给 ERC777 ,不忍从技术角度说几句公道话。要把锅全甩给 ERC777 ,是特朗普坏(甩锅给你,只因你太优秀)。
ERC777 是一个好的 Token 标准, 可以极大的提高 DeFi 应用的用户体验,通过使用的 Hook 回调机制,在 ERC20 中需要二笔或多笔完成的交易(当然还有其他的特性),而使用 ERC777 单笔交易就可以完成。
对行业的发展我一直是乐观派, 如果因为本次攻击,拒绝使用 ERC777,那一定在开历史倒车。这次事件挫败了大家对 DeFi 的信心, 从长远看,我相信会让行业更健康。
可重入攻击是怎么发生的?
下面我用一段简洁的代码说明可重入攻击是如何发生的(警告,以下是代码请勿使用),下面是 DeFi 应用最常见的逻辑,deposit 函数用来存款,存款时会记录下用户的存款金额,withdraw 函数用来取款,取款在余额的基础上加上一个利率。
在交互过程中,存在 3 个角色,用户、DeFi 合约、Token 合约, 用户存款和取款的时序图是这样的:
此时一切运行正常,(经过测试后)用户在一段时间之后可以赎回 110 个 Token,开开心心发布上线了。
后来上线了一个 ERC777 代币, ERC777 定义了以下两个 hook 接口:
用来同时发送者和接收者进行相应的响应,当然发送者和接收者也可以选择不响应(不实现接口)。
ERC777 的转账实现一般类似下面这样:(transfer 和 transferFrom 实现差不多,下面用 transfer 举例)
简单来说,就是在更改发送者和接收者余额的前后查看是否需要通知发送者和接收者,大部分情况下,普通账号对普通账号的转账(因为普通一般不会实现接口)和 ERC20 效果上一样的。
如果发送者和接收者实现了 ERC777 的转账接口, 上面的存款调用时序图就是这样的:
在 DeFi 合约调用 Token 的 transferFrom 时,Token 合约会调用 tokensToSend 和 tokenReceived 以便发送者和接收者进行相应的响应。注意这里 tokensToSend 由用户实现,tokenReceived 由 DeFi 合约实现。
这个回调能做很多有趣的事情,比如:可以把授权和存款合并为一笔交易,用户直接调用 Token 合约的转账,DeFi 合约收到转账后,在 tokenReceived 中完成用户的存款操作。
ERC777 协议没有对用户如何实现 tokensToSend 及 tokenReceived 做出规定,DeFi 合约开发者也不应该对参与方的实现进行任何的假定。在 Lendf.me 的攻击案例中,黑客用户就是在 tokensToSend 的实现中,调用了 DeFi 合约的 withdraw ,黑客用户合约的代码大概是这样的:
黑客攻击的时序图如下:
注意 tokensToSend() 、 withdraw() 和 tokensReceived() 函数都是在 transferFrom() 中执行的,根据 deposit 的代码:
只要前面 3 个函数没有出错,transferFrom 执行成功之后,就重置用户余额(黑客合约)为 100(存款金额)。而实际上黑客已经把所有存款全部取出,从而实现了一次对 DeFi 合约的攻击。
大家都没方法控制合约的实现,但是甩锅到 ERC777 对吗?那么对于 DeFi 开发者,如何避免攻击呢?
避免 ERC777 重入攻击
其实可重入攻击一直都存在,OpenZeppelin 也给过解决方案,给 DeFi 合约加上重入限制即可。
给 deposit 和 withdraw 函数加入重入限制后,此时如果在 tokensToSend 中调用 withdraw 就会失败而回退交易。很明显在 DeFi 合约中可以避免重入攻击。
最后希望 Lendf.me 度过难关。
END
了解最新动态