2025年智能合约十大漏洞(包含真实攻击案例及预防方法)

  • hacken
  • 发布于 4小时前
  • 阅读 54

本文深入探讨了智能合约中十大最关键的漏洞,包括重入攻击、整数溢出/下溢、不当访问控制、抢跑攻击、拒绝服务(DoS)攻击、弱随机性、易受攻击的外部调用、逻辑错误、Oracle操纵和闪电贷攻击。文章详细解释了每种漏洞的成因、常见的攻击方式,并提供了相应的缓解措施和代码示例,强调了安全审计、使用安全库以及遵循最佳实践的重要性。

智能合约可以自动化和保护区块链交易,而无需中介,这使得它们对于 Web3 和去中心化应用至关重要。然而,它们容易受到漏洞的影响。在 2024 年第一季度,智能合约漏洞导致了几乎 $45 million 的损失,共发生了 16 起事件,平均每次漏洞利用造成 280 万美元的损失。本文探讨了 10 个最关键的智能合约漏洞以及如何缓解它们。

智能合约漏洞的类型

智能合约漏洞可以分为几种类型,每种类型都会对区块链应用程序构成重大风险。 了解这些漏洞对于开发强大且安全的智能合约至关重要。

漏洞 描述
重入 (Reentrancy) 利用合约的外部调用功能,允许在初始函数完成之前重复调用。
整数溢出/下溢 (Integer Overflow/Underflow) 由算术运算超过数据类型的存储容量引起。
不正确的访问控制 (Improper Access Control) 由于访问限制不足,允许未经授权的用户访问或修改合约数据或函数(例如,未受保护的提款)。
抢跑交易 (Front-Running) 利用交易广播和将其包含在区块链中的时间差。
拒绝服务 (DoS) 通过消耗所有可用 gas 或导致交易持续失败,使合约不可用或无响应。
弱随机性 (Weak Randomness) 使用不安全的与区块相关的方法来生成随机数,这些随机数可以被操纵。
易受攻击的外部调用 (Vulnerable External Calls) 与在没有适当验证的情况下进行外部调用相关的风险。
逻辑错误 (Logic Errors) 包括合约逻辑中的缺陷,导致意外行为。
预言机操纵 (Oracle Manipulation) 扭曲预言机价格或其他链下数据以窃取资产。
闪电贷攻击 (Flashloan Attacks) 使用无抵押贷款来操纵市场或利用合约漏洞。

智能合约漏洞是如何发生的

智能合约漏洞通常由常见的编码错误或逻辑错误引起。 未经检查的外部调用、不正确的验证和算术错误等问题可能导致漏洞。 让我们深入研究具体的漏洞以及它们的缓解措施。

1. 重入 (Reentrancy)

当合约在更新其状态之前对另一个合约进行外部调用时,就会发生重入攻击。 然后,被调用的合约可以回调到原始合约,从而导致意外行为。

重入的类型

有几种类型的重入攻击:

  • 单函数重入 (Single-Function Reentrancy):攻击者重复调用同一个函数。
  • 跨函数重入 (Cross-Function Reentrancy):一个函数中的漏洞可能会利用共享相同状态变量的其他函数。
  • 跨合约重入 (Cross-Contract Reentrancy):共享相同状态的两个合约未在进行跨合约调用之前立即更新。
  • 只读重入 (Read-Only Reentrancy):重新进入 iew 函数可能会导致状态值不一致,从而导致其他协议读取不正确的数据并采取意外操作。

重入漏洞的代码示例

contract Deposit {

    mapping(address => uint) userBalance;

    function deposit() external payable {

        userBalance[msg.sender] = msg.value;

    }

    // 这个函数容易受到重入攻击

    function withdraw() external {

        require(userBalance[msg.sender] >= 0);

        (bool sent,) = payable(msg.sender).call{value: userBalance[msg.sender]}("");

        require(sent, "Failed to send Ether");

        userBalance[msg.sender] = 0;

    }

}

interface IDeposit {

    function deposit() external payable;

    function withdraw() external;

}

contract AttackDeposit {

    IDeposit private depositContract;

    constructor(address _target) {

        depositContract = IDeposit(_target);

    }

    function attack() external payable {

        require(msg.value == 1 ether, "Invalid attack amount");

        depositContract.deposit{value: msg.value}();

        depositContract.withdraw();

    }

    receive() external payable {

        if(address(depositContract).balance >= 1 ether) {

            depositContract.withdraw();

        }

    }

}

当我们向用户发送他们请求数量的以太币时,就会出现漏洞。 在这种情况下,攻击者会利用 withdraw() 函数。 因为他们的余额尚未重置为 0,所以他们可以转账 Token,即使已经收到了一些 Token。 该攻击涉及调用受害者合约中的 withdraw 函数。 收到 Token 后,receive 函数会无意中再次触发 withdraw 函数。 由于检查通过,合约会将 Token 发送给攻击者,随后激活 receive 函数。

涉及重入攻击的加密货币黑客事件

Rari Capital 黑客事件(8000 万美元)

2022 年 4 月 30 日,去中心化借贷平台 Rari Capital 由于其从 Compound 借用的代码中的缺陷而遭到黑客攻击。 borrow 函数缺少适当的检查-效果-交互模式。 攻击者通过以下方式利用了这一点:

  1. 获得 150,000,000 USDC 闪电贷。
  2. 将其存入 fUSDC-127 合约。
  3. 调用易受攻击的 borrow 函数来借用资产。
  4. 该函数在更新 accountBorrows 映射之前转移了借入金额。 在没有重入保护的情况下,攻击者在映射更新之前重复调用 borrow 函数,耗尽了 8000 万美元。
function borrow() external {

    …

    doTransferOut(borrower, borrowAmount);

    // doTransferOut: function doTransferOut(borrower, amount) {

    (bool success, ) = to.call.value(amount)("");

    require(success, "doTransferOut failed");

    }

    // !!状态更新在转移后进行

    accountBorrows[borrower].principal = vars.accountBorrowsNew;

    accountBorrows[borrower].interestIndex = borrowIndex;

    totalBorrows = vars.totalBorrowsNew;

    …

}

黑客使用闪电贷借入资产,并在循环中运行 doTransferOut 函数五次。 在偿还闪电贷后,他们拿走了剩余的资金,并带着 8000 万美元消失了。 交易:​​0xab486012

Orion 协议(300 万美元)

2023 年 2 月 2 日,Orion 协议由于其核心合约之一中的重入漏洞而遭到黑客攻击,导致 $3 million loss。 攻击者利用 ExchangeWithOrionPool 合约的 depositAsset() 方法,该方法缺乏重入保护。 他们创建了一个具有自毁功能的虚假 Token (ATK),从而导致 transfer() 函数。

重入缓解

使用检查-效果-交互模式,以确保状态更改发生在外部调用之前。

存在漏洞的实现

mapping (address => uint) public balances;

function withdraw(uint _amount) public {

    require(balances[msg.sender] >= _amount);

    (bool success, ) = msg.sender.call{value: _amount}("");

    require(success);

    balances[msg.sender] -= _amount;

}

推荐的实现

function withdraw(uint _amount) public {

    require(balances[msg.sender] >= _amount);

    balances[msg.sender] -= _amount;

    (bool success, ) = msg.sender.call{value: _amount}("");

    require(success);

}

一般来说,为了防止重入攻击,Web3 项目可以:

  • 确保所有状态更改都发生在调用外部合约之前,即在调用外部代码之前更新余额或内部代码。
  • 使用防止重入的函数修饰符。 OpenZeppelin 提供了一个具有此功能的 ReentrancyGuard 合约。
  • 采用提取付款方法。
  • 实施紧急停止模式以防止易受攻击的代码多次执行。
  • 进行彻底的智能合约测试和审计。

2. 整数溢出/下溢 (Integer Overflow/Underflow)

当算术运算超过数据类型的存储容量时,就会发生整数溢出和下溢,从而导致意外结果。

当值减小到零以下时,会发生下溢,而当值超过其最大值时,会发生溢出。 这些漏洞会导致智能合约中出现意外行为,可能导致财务损失或系统故障。

考虑一个 uint8 变量,它可以容纳最多 8 位。 这意味着它可以存储的最高数字在二进制中表示为 11111111(或在十进制中表示为 2^8 - 1 = 255)。 如果发生下溢,从设置为 0 的 uint8 中减去 1 将导致其值回绕到 255。相反,当尝试将 1 添加到设置为 255 的 uint8 时,会发生溢出,从而导致该值重置回 0。

整数溢出/下溢漏洞的代码示例

contract MyContract {

    uint256 public a = type(uint256).min; // uint256 的最小值,即 0

    uint256 public b = type(uint256).max; // uint256 的最大值,即 2^256 - 1

    function add() external {

        // b == 115792089237316195423570985008687907853269984665640564039457584007913129639935

        b = b + 1; // 这会导致整数溢出

        // 递增后,b 回绕到 0

        // b == 0

    }

    function substract() external {

        // a == 0

        a = a - 1; // 这会导致整数下溢

        // 递减后,a 回绕到 uint256 的最大值

        // a == 115792089237316195423570985008687907853269984665640564039457584007913129639935

    }

}

涉及整数溢出/下溢的加密货币黑客事件

Poolz Finance 黑客事件(39 万美元)

2023 年 3 月 15 日,由于未经审计的 LockedControl 智能合约中的整数溢出漏洞,Poolz Finance 合约遭到黑客攻击,导致至少 $390K 的损失,涉及 BSC 和 Polygon。 攻击者通过操纵 GetArraySum() 方法来利用溢出,该方法使总和超过其最大限制,从而允许他们将过多的 Token 提取到他们的钱包中。

PoWHC 黑客事件 (80 万美元)

弱手币 (PoWHC) 的证明(本身就是一个庞氏骗局)由于整数下溢漏洞而被利用,导致黑客窃取了 866 ETH。 ERC-20 实现的 "approve" 函数中的漏洞导致第二个帐户的余额被错误地调整,从而导致余额膨胀。 通过操纵 transferFrom()transferTokens() 函数,攻击者导致第二个帐户的余额下溢到 2²⁵⁶-1,从而实现了盗窃。

整数溢出/下溢缓解

随着 Solidity 0.8 的发布,这个问题已得到解决。 编译器现在会自动验证每个算术运算是否存在溢出和下溢,如果检测到,它会抛出一个错误。 这减轻了开发人员的负担,因为他们不再需要手动处理这些问题。

对于低于 0.8 版本的 Solidity,请使用 SafeMath 库来防止溢出和下溢。 该库提供了防止溢出和下溢漏洞的函数,从而确保智能合约中算术运算的完整性。

存在漏洞的实现

function transfer(address _to, uint256 _value) public {

    balances[msg.sender] -= _value;

    balances[_to] += _value;

}

推荐的实现

using SafeMath for uint256;

function transfer(address _to, uint256 _value) public {

    balances[msg.sender] = balances[msg.sender].sub(_value);

    balances[_to] = balances[_to].add(_value);

}

3. 不正确的访问控制 (Improper Access Control)

访问控制漏洞允许未经授权的用户访问或修改合约的数据或函数。 当代码未能根据权限限制访问时,就会出现这些漏洞。 在智能合约中,访问控制问题会影响治理和关键功能,例如铸造 Token、投票、提取资金、暂停/升级合约以及更改所有权。

常见的访问控制漏洞

  • 错过的修饰符验证 (Missed Modifier Validations):关键函数中缺少验证可能会导致合约泄露或资金损失。
  • 不正确的修饰符名称 (Incorrect Modifier Names):修饰符名称中的错误可能会绕过控制,从而导致资金损失或未经授权的更改。
  • 过多的角色权限 (Overpowered Roles):过多的权限会产生漏洞; 请使用最小权限原则。

不正确的访问控制的代码示例

function mint(address account, uint256 amount) public {

    // mint 函数未实现正确的访问控制

    _mint(account, amount);

}

涉及不正确的访问控制的加密货币黑客事件

HospoWise 黑客事件

HospoWise 由于公共销毁函数而被黑客入侵,从而允许任何人销毁 Token。 该代码的销毁函数缺乏访问控制,从而使攻击者可以销毁 UniSwap 上的所有 Hospo Token,从而导致通货膨胀并耗尽 ETH 池。

预防措施:正确的访问控制,例如 onlyOwner,或使该函数成为 internal 类型。

Rubixy 黑客事件

由于构造函数命名错误,Rubixy 被利用。 函数 Fal1out 应该是构造函数,但任何人都可以调用它,从而允许攻击者声明所有权并耗尽资金。

预防措施:使用正确的构造函数语法和仔细的合约命名。

不正确的访问控制缓解

  • 仅授予实体执行其任务所需的最低访问级别 - 最小权限原则 (POLP)。 此原则有助于防止未经授权的当事方执行破坏性操作,例如耗尽资金或更改关键变量。
  • 通过使用访问控制修饰符(例如来自 OpenZeppelin 的 Ownable 合约的 onlyOwner)或通过使用基于角色的访问控制 (RBAC) 来定义具有不同权限的特定角色,从而实现 PoLP。
// 此代码尚未经过专业审核。 使用风险自负。

pragma solidity ^0.8.20;

import "@openzeppelin/contracts/access/AccessControl.sol";

contract MyContract is AccessControl {

    bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");

    bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");

    address public oracle;

    address public treasury;

    constructor(address minter) {

        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);

        _grantRole(MANAGER_ROLE, minter);

    }

    function setOracleAddress(address _oracle) external onlyRole(MANAGER_ROLE) {

        require(_oracle != address(0), "Invalid oracle address!");

        oracle = _oracle;

    }

    function setTreasuryAddress(address _treasury) external onlyRole(ADMIN_ROLE) {

        require(_treasury != address(0), "Invalid treasury address!");

        treasury = _treasury;

    }

}

在上面的代码中,RBAC 根据预定义的角色分配用户权限,从而提供精细的控制。 它支持多个角色,例如 onlyAdminRole 和 onlyModeratorRole,从而通过限制对必要功能的访问来增强安全性。 OpenZeppelin 的 AccessControl 合约简化了智能合约中 RBAC 的实现。

4. 抢跑交易攻击 (Front-Running Attack)

当恶意行为者通过提交具有更高 gas 费的类似交易来抢占交易时,就会发生抢跑交易。 自 2020 年 6 月以来,运营机器人的最大可提取价值交易者(又名 MEV 机器人)已在 Ethereum、BSC 和 Solana 上获得了超过 $1 billion 的利润,通常会损害散户投资者。

三种类型的抢跑交易攻击

  1. 替换 (Displacement):使用更高的 gas 费来优先处理他们的交易。
  2. 抑制 (Suppression):用高额费用交易淹没网络以延迟其他交易。
  3. 插入 (Insertion):将受害者的交易夹在他们自己的两个交易之间,以从价格变化中获利。

抢跑交易缓解

在竞标期结束后,使用提交-揭示方案来隐藏竞标细节。

存在漏洞的实现

function placeBid(uint256 _bid) public {

    require(_bid > highestBid);

    highestBid = _bid;

}

推荐的实现

function placeBid(bytes32 _sealedBid) public {

    sealedBids[msg.sender] = _sealedBid;

}

function revealBid(uint256 _bid, bytes32 _secret) public {

    require(sealedBids[msg.sender] == keccak256(abi.encodePacked(_bid, _secret)));

    require(_bid > highestBid);

    highestBid = _bid;

}

为了保护交换应用程序,请根据网络费用和交换规模在 0.1% 到 5% 之间实施滑点限制。 这会最大限度地减少滑点,从而防御利用更高利率的抢跑者,从而保护你的交易并降低掠夺性风险。

有关抢跑交易攻击的详细指南,并了解其他缓解策略,请参阅 区块链中的抢跑交易:真实案例和预防

5. 拒绝服务 (DoS) 攻击

DoS 攻击可能会通过利用恢复、外部调用失败和 gas 限制问题来破坏合约功能,从而使合法用户无法使用。

意外恢复导致的 DoS

当合约操作失败时,它会恢复更改。 EVM 使用 REVERT (0xFD) 和 INVALID (0xFE) 操作码来处理这些错误,其中 REVERT 将剩余的 gas 返回给调用者,而 INVALID 不返回任何 gas。

意外恢复导致的 DoS 示例

当合约尝试使用 call 方法发送 1 以太币时,会发生意外恢复。 如果接收方是在收到以太币时恢复的合约,则交易失败,从而阻止了受益人标志被重置。 如果相同的地址重复尝试提取,这可能会导致 DoS 状况,从而阻止进一步的提取。

function withdraw() public {

    require(beneficiaries[msg.sender]);

    beneficiaries[msg.sender] = false;

    (bool success, ) = msg.sender.call{value: 1 ether}("");

    require(success);

}

意外恢复导致的 DoS 缓解

使用 pull 而不是 push 付款模式来防止 DoS。 pull 模式将提取资金的责任转移到接收方,从而防止合约因转移失败而被锁定。 完全缓解的代码将存储待处理的提取,并允许用户声明它们。

function withdraw() public {

    require(beneficiaries[msg.sender]);

    beneficiaries[msg.sender] = false;

    payable(msg.sender).transfer(1 ether);

}

外部调用失败

外部调用可能会意外或故意失败,从而导致 DoS 状况。

外部调用失败缓解

让用户提取资金,而不是自动将资金 push 给他们。

  • 避免在单个交易中合并多个调用,尤其是在循环中。
  • 假设外部调用可能会失败。
  • 实施逻辑来处理失败的调用。

Gas 限制漏洞

大型数组或循环可能会超过区块 gas 限制,从而导致 DoS 状况。

Gas 限制漏洞缓解

  • 避免循环遍历大型数组。
  • 规划操作以在必要时占用多个区块和交易。

6. 弱随机性 (Weak Randomness)

由于以太坊的确定性性质,在其上生成随机数具有挑战性。 Solidity 依赖于伪随机因素,并且复杂的计算在 gas 方面成本高昂。

弱随机数生成方法

智能合约开发人员通常使用不安全的与区块相关的方法来生成随机数,例如当前区块时间戳、难度、编号、当前矿工的地址或给定区块的哈希。 但是,这些方法可能不安全,因为矿工可以操纵它们,从而影响合约的逻辑。

function guess(uint256 _guess) public {

        uint256 answer = uint256(keccak256(abi.encodePacked(block.timestamp, block.difficulty, msg.sender)));

}

弱随机性缓解

  • 使用预言机:采用外部随机数来源,如 Chainlink VRF,它提供可证明的公平和可验证的随机数。
  • 承诺方案:使用提交-揭示方法,这些方法在掷Coin、零知识证明和安全计算(例如,RANDAO)中都有应用。
  • Signidice 算法:使用适用于涉及双方的应用程序中 PRNG 的加密签名。

7. 易受攻击的外部调用 (Vulnerable External Calls)

在没有适当验证的情况下进行外部调用(例如,未经检查的调用或对任意地址的调用)可能会导致意外行为或安全风险。

未经检查的外部调用

该函数不会验证调用的成功或失败。 即使外部调用失败,交易也会继续进行,这可能会导致意外行为或漏洞。

function externalCall(address _to) public {

    (bool success, ) = _to.call("");

    require(success);

}

经过检查的外部调用

调用已验证,并且已处理失败。

function externalCall(address _to) public {

    require(isValidAddress(_to));

    (bool success, ) = _to.call("");

    require(success);

}

function isValidAddress(address _addr) internal pure returns (bool) {

    return _addr != address(0);

}

对任意地址的外部调用

对任意地址的调用是指与未预先确定或信任的外部地址的交互。 攻击者可以利用此缺陷来运行未经授权的代码、提取资产或破坏合约的功能。

涉及易受攻击的外部调用的加密货币黑客事件

Dexible 漏洞(200 万美元)

2023 年 2 月 20 日,Dexible DEX 聚合器和执行管理系统的自我交换功能因其外部调用漏洞而被利用,该漏洞允许用户定义路由合约。

contract Dexible {

    function selfSwap(address tokenIn, address tokenOut, uint112 uint112 amount, address router, address spender, TokenAmount routeAmount, bytes routerData) external {

        IERC20(routeAmount.token).safeApprove(spender, routeAmount.amount);

        // 此处对路由器进行外部调用

        (bool s, ) = router.call(routerData);

    }

}

黑客没有使用安全有效的 DEX,而是制作了一个合约来调用恶意的 ERC20 合约,并耗尽了价值 200 万美元的 Token。 最关键的部分是合约没有经过审计。

contract maliciousRouter {

    ...

    //黑客没有实现经过验证的路由合约,而是实现了这个棘手的函数,并使 Dexible 合约将其资产转移到这个恶意的合约。

    function transfer() external {

        IERC20(USDC).transferFrom(msg.sender, address(this), IERC20(USDC).balanceOf(msg.sender));

    }

}

易受攻击的外部调用缓解

  • 进行外部调用时要小心:对不受信任的合约的调用可以执行恶意代码。 将每个外部调用视为潜在的安全风险,并遵循最佳实践以最大限度地降低危险。
  • 标记不受信任的合约:清楚地命名变量、方法和合约接口,以指示与外部合约交互时的潜在风险。
  • 避免在外部调用后更改状态:恶意代码可以劫持外部调用期间的控制流,从而导致重入等漏洞。 避免在进行外部调用后更改状态。
  • 不要使用 transfer() 或 send()transfer()send() 准确转发 2,300 gas,由于 gas 成本不断变化,这可能不足以满足接收方的需求。 请改用 .call() 并检查返回值。
  • 处理外部调用中的错误:谨慎使用底层调用方法,并始终检查返回值以处理失败。
  • 对于外部调用,最好提取而不是推送:将每个外部调用隔离到其自己的交易中,尤其是在付款方面。 让用户提取资金,而不是自动将资金推送给他们,以避免 gas 限制问题。
  • 不要将 delegatecall 用于不受信任的代码:对不受信任的合约使用 delegatecall 可能会导致状态更改和合约余额的潜在损失。

8. 逻辑错误 (Logic Errors)

智能合约中的逻辑错误会导致意外行为,从而危及安全性和功能。

逻辑错误示例

该函数盲目地将提供的金额添加到发送者的余额中,而没有进行验证,从而导致潜在的问题,例如溢出和无效输入。

function updateBalance(int256 _amount) public {

    balances[msg.sender] += _amount;

}

导致逻辑错误的原因

  • 开发人员负担过重:开发人员可能因任务负担过重而错过了此验证步骤。
  • 缺乏专业知识:对智能合约开发的最佳实践和潜在陷阱的了解不足。
  • 测试不足:该函数可能未经过彻底的测试以发现此逻辑错误。

逻辑错误缓解

总体思路是实施彻底的测试和代码审查,以检测和修复逻辑错误。

例如,经过缓解的代码添加了一个验证检查,以确保在更新余额之前金额为正数,从而防止溢出和无效输入问题。

function updateBalance(int256 _amount) public {

    require(_amount != 0, "Invalid amount");

    balances[msg.sender] = balances[msg.sender].add(_amount);

}

9. 预言机操纵 (Oracle Manipulation)

预言机是区块链通往现实世界的门户。 它们将智能合约连接到链下数据(现实世界事件、价格摘要、随机数生成)。 但是,预言机操纵会通过欺骗、拉升、熊市袭击、跨市场操纵、虚假交易和抢跑交易等方法来显着扭曲市场价格。

涉及预言机操纵的加密货币黑客事件

Inverse Finance (1560 万美元)

攻击者使用 SushiSwap 的 TWAP 预言机操纵了 INV Token 的价格,通过存入膨胀的 INV Token 作为抵押品,借入了 1560 万美元。 依赖单一预言机是一个主要问题。

Lodestar (650 万美元)

一个不良行为者操纵了 plvGLP 抵押品的价格预言机,使他们能够耗尽贷款池并获利约 650 万美元。 核心漏洞在于 GLPOracle 如何计算 plsGLP 的价格。 攻击者通过捐赠函数增加总资产来操纵价格,这推高了价格,并允许攻击者借用比其抵押品的真实价值更多的资金。

BonqDAO (180 万美元)

由于智能合约代码错误,Polygon DeFi 协议 BonqDAO 成为了价格预言机攻击的受害者。 攻击者窃取了 1 亿个 $BEUR 稳定币和 1.2 亿个 $WALBT。 漏洞是由智能合约内部的价格摘要漏洞启用的,该智能合约使用 Tellor 预言机的 ALBT 价格向 Bonq 协议提供价格。

AaveV3(已阻止)

后备预言机中的漏洞允许攻击者设置任意资产价格,从而构成重大的安全风险。2024年5月16日,由于 Compound V2 forks 中一个已知的漏洞,Sonne Finance 被攻击,损失了 2000 万美元。尽管之前的事件发出了警告,但该协议未能实施全面的安全措施,使得攻击者能够操纵治理权限,并使用闪电贷耗尽资金。

闪电贷攻击缓解

  • 审计智能合约:定期进行安全审计,以识别和修复漏洞。全面的安全审计可以发现合约逻辑和实现中潜在的弱点。
  • 要求贷款提供抵押品,这会增加黑客借用资产的成本。
  • 实施可以提取的闪电贷规模的限制,这有助于减轻大规模攻击。
  • 在智能合约中添加时间锁,以阻止过快偿还闪电贷的能力,从而使开发人员有时间检测和防止攻击。
  • 具有治理投票系统的项目必须实施反闪电贷机制,以防止攻击者使用闪电贷来获得多数投票权。

了解更多关于 闪电贷攻击 & 预防 的信息。

智能合约的安全模式

安全模式对于开发健壮的智能合约至关重要。关键模式包括:

  • Checks-Effects-Interactions Pattern: 实施 Checks-Effects-Interactions 模式,以确保状态更改发生在外部调用之前,并使用 Reentrancy Guard 来防止重入攻击。
  • Mutex Pattern: 通过在执行期间锁定合约来防止重入。
  • Balance Limit Pattern: 限制合约的余额以降低风险。

安全智能合约开发的最佳实践

通过遵循这些最佳实践,开发人员可以构建一个强大的防御系统,从而加强其 智能合约的安全性

01. **重用经过审计的库**:使用像 OpenZeppelin 这样成熟的库,并彻底阅读文档以理解和正确使用代码。
02. **测试你的代码**:目标是 100% 的分支覆盖率,使用多个用户测试,并使用测试 [模糊测试](https://hacken.io/discover/fuzzing-for-blockchain/)(例如,Foundry 框架)来检测错误。
03. **申请审计**:考虑专业的审计,例如 Hacken 的审计,以识别潜在的漏洞。
04. **使用 SafeERC20 库**:在处理 ERC20 代币时,使用 SafeERC20 库来处理可能失败的操作。
05. **使用 MultiSig 钱包**:MultiSig 钱包可用于合约所有权,以提高安全性并减少单点故障。
06. **使用锁定的 Pragma 版本**:锁定 Solidity 版本以避免已知的错误并确保稳定性(例如,使用 0.8.10 而不是 ^0.8.10)。
07. **小心地与第三方合约交互**:验证第三方合约是否存在错误、审计和正确的使用,以最大限度地减少外部调用。
08. **谨慎使用 Delegatecall**:理解 `delegatecall` 在调用合约的上下文中执行代码,从而保留 `msg.sender` 和 `msg.value`。
09. **使用 msg.sender 进行身份验证**:首选 `msg.sender` 而不是 `tx.origin`,以提高安全性并防止授权漏洞。
10. **避免算术溢出和下溢**:使用 0.8 以上的 Solidity 版本,或者为更早的版本使用 SafeMath 库。
11. **使用静态代码分析器**:使用像 Slither 这样的工具进行静态代码分析,以检测漏洞并提高代码质量。
12. **验证函数参数**:确保验证所有用户提供的参数,以防止恶意输入。
13. **注意整数除法舍入**:使用乘数来提高精度,或者单独存储分子和分母。
14. **显式标记可见性**:为了避免意外访问,请为函数和存储变量定义可见性。
15. **避免使用 extcodesize 检查 EOA。**`extcodesize` 在合约构建期间返回零,这可能会导致潜在的问题。
16. **限制对关键函数的访问**:实施访问控制以限制重要函数。
17. **使用紧急停止模式**:实施 Pausable 合约以在紧急情况下停止操作。
18. **首选拉取而不是推送模式**:使用拉取模式来安全地处理付款。

总结

鉴于概述的广泛的潜在漏洞——从高亮显示的特定示例到我们的智能合约审计清单中确定的 30 多个额外问题——区块链领域的威胁形势是复杂且多方面的。智能合约开发人员在创建尖端应用程序和建立安全措施以保护这些合约免受不断演变的威胁方面起着关键作用。他们面临的挑战性任务是领先于这些风险,确保其应用程序的安全性、完整性和可信赖性。

Web3 项目必须优先考虑其区块链应用程序的安全性。通过遵守准则并主动解决漏洞,开发人员可以建立用户信任、保护资产,并为区块链生态系统的稳定和增长做出贡献。优先考虑安全保障个人项目并加强整个去中心化金融领域。让我们共同努力,为每个人创建一个更安全、更可靠的区块链环境。

  • 原文链接: hacken.io/discover/smart...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
hacken
hacken
江湖只有他的大名,没有他的介绍。