从 approve 函数看 DeFi

谈国鹏热度: 36591

DeFi 最核心的挑战就是智能合约的安全性。一旦某个合约出现重大安全漏洞,也很有可能导致所有资产被黑客卷走

最近 DeFi 大热,尤其以 Compound 为代表,非常火热。简单来说,可以把 DeFi 理解成去中心化的 P2P 借贷。把借贷规则通过智能合约公开化、透明化,并且资金池也是去中心化的,可以随时存取。从而规避了传统 P2P 借贷的众多弊病。

DeFi 最核心的挑战就是智能合约的安全性。一旦某个合约出现重大安全漏洞,也很有可能导致所有资产被黑客卷走。虽然合约代码都是开源的,并且一般通过好几个公司审计(个人觉得这都是忽悠观众的,一直觉得代码审计公司就是一个很莫名的存在),但并不代表安全。

我仔细研究一下 Compound,发现了一些隐忧之处。以进行 USDT 借贷为例,借款方的行为是:存入 USDT,获取 cUSDT。存入 USDT 的操作的具体步骤是:

  1. 调用 USDT contract 对 cUSDT 的 contract 进行 approve;

  2. 调用 cUSDT contract 的 mint 方法进行存入 USDT。

可能有人不太理解 ERC20 的 approve 方法,简单解释一下,自己(私钥账户或合约账户)向外 transfer token 是不需要进行 approve 的,但是在他人的合约中通过 transferFrom 进行代币转移是需要提前进行 approve 的。

approve 方法本身也有一些 check,要求每次被 approve 的账户都是干净的,下面以 USDT contract 的 approve 源码为例:

        /**    * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender.    * @param _spender The address which will spend the funds.    * @param _value The amount of tokens to be spent.    */    function approve(address _spender, uint _value) public onlyPayloadSize(2 * 32) {
    // To change the approve amount you first have to reduce the addresses` // allowance to zero by calling `approve(_spender, 0)` if it is not // already 0 to mitigate the race condition described here: // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 require(!((_value != 0) && (allowed[msg.sender][_spender] != 0)));
    allowed[msg.sender][_spender] = _value; Approval(msg.sender, _spender, _value); }

    从里面的 require 可以看出,要么 _value 传入 0,对某人进行清零,要么某人已经是0了,才可以进行新的 approve。

    举例说明,你 approve 小明可以转移 100 USDT,然而小明只转移了 50 USDT。此时你再次 approve 小明可以转移 200 USDT,将是失败的操作。此时你只有先对小明清零,再进行 approve,或者要求小明转移走剩余的 50 USDT。

    也就是说 ERC20 合约是对 approve 和 transferFrom 做了以上规则和限制的,以保证你的 token 安全。

    我用 Compound 官方 Dapp 进行了 USDT 存入,为了规避以上的麻烦事,我发现他们是这样调用的:

      Function: approve(address _spender, uint256 _value)
      MethodID: 0x095ea7b3[0]: 000000000000000000000000f650c3d88d12db855b8bf7d11be6c55a4e07dcc9[1]: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

      天!这意味着什么?这意味着 cUSDT 合约可以转走我的账号里的任意多的 USDT,而我实际上实验的只是10 USDT。

      这就是所谓的潜在漏洞,也许今天不会发生什么。但是某天一个合约功能的升级,或者其他的一个bug被黑客抓到,这个漏洞可能会引发灾难,黑客可能可以从所有用过 Compound 合约的账号里转移任意资产,因为你允许它转的(看上面的 ffff 表示很大的一个数字)。

      想想你的账户安全吗?被这样的代码执行过之后,将来你有钱了,你挣了 100万 USDT,然后突然发现它被人划走了,被 cUSDT 合约账户划走了,因为 cUSDT 账户被某个黑客操纵了。

      我已经向 Compound 官方提交了这个问题,虽然这个问题现在不会引起什么问题,但是我们也不应该留下这样的隐患。

      DeFi 把合约对安全的要求提高到了全新的高度。以太坊的智能合约功能就像潘多拉的盒子,非常的强大,但也充满着各种危险和陷阱!

      声明:本文为入驻“火星财经 专栏”作者作品,不代表火星财经官方立场。
      转载请联系网页底部:内容合作栏目,邮件进行授权。授权后转载时请注明出处、作者和本文链接。未经许可擅自转载本站文章,将追究相关法律责任,侵权必究。
      提示:投资有风险,入市须谨慎,本资讯不作为投资理财建议。
      本内容旨在传递行业动态,不构成投资建议或承诺。