Solidity 合约安全,常见漏洞(第四篇)

本文深入探讨了Solidity智能合约中的安全问题,包括管理员权限过大、四舍五入错误、抢跑(Frontrunning)攻击、签名相关漏洞、以及多种边缘案例。强调了权限最小化、安全签名使用、防止抢跑攻击的重要性,同时提醒开发者注意合约的不可变性和潜在的算术溢出问题。此外,文章列举了真实世界的黑客事件,展示了实际应用中的安全漏洞和攻击载体。
摘要由CSDN通过智能技术生成

Solidity 合约安全,常见漏洞(第四篇)

权力过大的管理员

仅仅因为一个合约有一个所有者或管理员,这并不意味着他们需要无限权力。考虑一个 NFT。按理说,只有所有者才能从 NFT 销售中提取收益,但如果所有者的私钥被泄露,能够暂停合约(阻止转账)就会造成严重的破坏。一般来说,管理员的权限应该尽可能的小,以减少不必要的风险。

使用 Ownable2Step 而不是 Ownable

这在技术上不是一个漏洞,但OpenZeppelin ownable如果所有权被转移到一个不存在的地址,会导致合约所有权的丧失。Ownable2step 要求接收者确认所有权。这可以防止意外地将所有权发送到一个错误的地址。

四舍五入的错误

Solidity 没有浮点,所以舍入错误是不可避免的。设计者必须意识到正确的做法是向上舍入还是向下舍入,以及舍入应该对谁有利。
除法应该总是最后进行。下面的代码在小数位数不同的稳定币之间进行了错误的转换。下面的兑换机制允许用户在兑换 dai(精度为 18)时免费获得少量的 USDC(精度为 6)。变量 daiToTake 将四舍五入为零,换取非零的 usdcAmount 时,用户拿不到任何东西。

contract Exchange {

  uint256 private constant CONVERSION = 1e12;

  function swapDAIForUSDC(uint256 usdcAmount) external pure returns (uint256 a) {
    uint256 daiToTake = usdcAmount / CONVERSION;
    conductSwap(daiToTake, usdcAmount);
  }
}

抢跑(Frontrunning)

在 Etheruem(和类似的链)的背景下,Frontrunning 意味着观察一个待定的交易,并通过支付更高的 交易成本在它之前执行另一个交易。也就是说,攻击者已经 "跑到了 "交易的前面。如果该交易是一个有利可图的交易,那么除了支付更高的 交易成本,完全复制该交易是有意义的。
这种现象有时被称为 MEV,意思是矿工可提取的价值,但有时在其他情况下是最大可提取的价值。区块生产者有无限的权力来重新排序交易和插入自己的交易,从历史上看,在以太坊进入股权证明之前,区块生产者就是矿工,因此而得名。

抢跑:不受限制的提款

从智能合约中提取以太币可以被认为是一种 “有利可图的交易”。你执行了一个零成本的交易(除了 Gas),最终拥有的加密货币比你开始时更多。

contract UnprotectedWithdraw {

    constructor() payable {
        require(msg.value == 1 ether, "must create with 1 eth");
    }

    function unsafeWithdraw() external {
        (bool ok, ) = msg.sender.call{value: address(this).value}("");
        require(ok, "transfer failed").
    }
}

如果你部署了这个合约并试图退出,一个先行者机器人会注意到你在 mempool 中对 "unsafeWithdraw "的调用,并复制它来先获得以太币。

抢跑:ERC4626 通膨攻击,是抢跑和四舍五入错误的组合

我们已经在ERC4626 教程中深入介绍了 ERC-4626 的通膨攻击。但它的要点是,ERC4626 合约根据交易者贡献的 "资产 "的百分比来分配 "份额"代币。大致上,它的运作方式如下:

function getShares(...) external {
    // code
    shares_received = assets_contributed / total_assets;
    // more code
}

当然,没有人会贡献资产而得不到任何股份,但他们无法预测这种情况会发生,如果有人能在交易中先发制人,获得股份。
例如,当池子里有 20 个资产时,他们贡献了 200 个资产,他们期望得到 100 份额。但是,如果有人在交易中提前存入 200 个资产,那么公式将是 200/220,四舍五入为零,导致受害者失去资产,获得零份额。

抢跑:ERC20 授权

我用一个真实的例子来说明这一点,而不是抽象地描述它:

  1. 假设 Alice 授权了 Eve 的 100 个代币。Eve 是邪恶的代表,而不是用 Bob ,所以我们将保持惯例。
  2. Alice 改变了主意,发送了一个交易,将 Ev e 的授权改为 50。
  3. 在将授权额度改为 50 的交易纳入区块之前,它位于 Mempool 中,Eve 可以看到它。
  4. Eve 发送了一个交易,要求获得她的 100 个代币,这在将授权改为 50 之前。
  5. 对 50 的授权的交易通过了。
  6. Eve 又获取了 50 个代币。

现在 Eve 有 150 个代币,而不是 100 或 50。解决这个问题的办法是,在处理不受信任的授权时,在增加或减少授权之前,将授权设置为零。

抢跑:三明治攻击

一项资产的价格会随着买卖压力的变化而变化。如果一个大订单在 Mempool 中,交易者有动力去复制这个订单,但要有更高的 gas 成本。这样一来,他们就会购买资产,让用户的大额订单使价格上涨,然后他们马上卖出。卖出订单有时被称为 “尾随”。卖出订单可以通过放置一个较低 gas 成本的卖出订单来完成,这样的序列看起来像这样的

  1. 抢跑买入
  2. 大额买入(用户)
  3. 卖出

对这种攻击的主要防御是提供一个 "滑点"参数。如果 "抢跑买入 "本身将价格推高到某个阈值以上,"大额买入"订单将回退,使抢跑者的交易失败。
这就是所谓的三明治攻击(sandwhich),因为大额买入被抢跑买入和尾随卖出夹在中间。这种攻击也适用于大额卖单,只是方向相反。

了解更多关于抢跑的信息

抢跑是一个巨大的话题。Flashbots已经对这个话题进行了广泛的研究,并发表了一些工具和研究文章,以帮助最大限度地减少它的负面外部因素。通过适当的区块链架构是否可以 "设计掉 "抢跑是一个争论不休的话题,还没有得到最终的解决。以下两篇文章是关于这个问题的永恒的经典之作:
以太坊是一个黑暗的森林
逃离黑暗森林

签名相关

数字签名在智能合约的背景下有两种用途:

  • 使得地址能够授权区块链上的一些交易,而不进行实际交易
  • 根据预定的地址,向智能合约证明发起者有某种权力去做某事

下面是一个安全使用数字签名的例子,让用户拥有铸造 NFT 的特权:

import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract NFT is ERC721("name", "symbol") {
  function mint(bytes calldata signature) external {
    addres
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值