10分钟智能合约:进阶实战-3.3 拒绝服务攻击

欢迎订阅专栏10分钟智能合约:进阶实战

智能合约拒绝服务攻击(DoS):原理、类型与防御

拒绝服务攻击(Denial of Service, DoS) 在智能合约中,指攻击者通过特定手段使合约无法正常提供服务,例如使关键函数永远失败、Gas 消耗过高无法执行、或合约状态被锁定。与传统 Web 的 DoS 不同,智能合约的 DoS 不仅影响可用性,还可能导致资金被锁、业务中断等严重后果。


1. 核心原理

智能合约的 DoS 攻击主要利用 EVM 的执行规则合约设计缺陷,使目标函数无法按预期完成。常见原理包括:

  • Gas 消耗过高:攻击者使函数执行所需的 Gas 超过区块 Gas 上限,导致交易无法被打包。
  • 外部调用失败:合约依赖的外部调用(如发送 ETH、调用其他合约)永久失败,导致主逻辑无法继续。
  • 状态锁定:关键状态变量被设置为无效值,导致权限检查永远失败。
  • 存储膨胀:大量占用存储,使后续操作 Gas 成本飙升。

2. 主要攻击类型与示例

2.1 Gas 耗尽型(Gas Exhaustion)

攻击者通过触发合约中的高复杂度循环无上限迭代,使函数执行 Gas 远超区块限制,导致交易永远无法成功。

典型场景:退款函数遍历所有参与者。

solidity 复制代码
// 漏洞示例:退款时遍历所有参与者
contract VulnerableAuction {
    address[] public bidders;
    mapping(address => uint) public bids;

    function refundAll() public {
        for (uint i = 0; i < bidders.length; i++) {
            address bidder = bidders[i];
            uint amount = bids[bidder];
            if (amount > 0) {
                bids[bidder] = 0;
                (bool success, ) = bidder.call{value: amount}("");
                require(success, "Refund failed"); // 一旦某个地址拒绝接收,全盘回滚
            }
        }
    }
}

攻击方式 :攻击者创建大量恶意地址(或部署拒绝接收 ETH 的合约)参与竞拍,导致 refundAll 循环 Gas 耗尽或中途失败。

2.2 外部调用失败型(External Call Failure)

合约假设外部调用必然成功,若调用失败且未妥善处理,则主逻辑无法继续。

典型场景:向用户发送 ETH 失败导致函数回滚,攻击者可故意让接收地址 revert。

solidity 复制代码
// 漏洞示例:必须成功发送ETH
function claimReward() public {
    require(rewarded[msg.sender] == false);
    uint amount = rewards[msg.sender];
    rewarded[msg.sender] = true;
    (bool success, ) = msg.sender.call{value: amount}("");
    require(success, "Transfer failed"); // 若用户拒绝,则无法领奖,且状态回滚,用户可反复尝试
}

攻击方式 :攻击者部署一个在 receive()revert() 的合约,然后调用 claimReward,交易总是失败,但攻击者并未得到奖励,也无法被标记为已领取,从而阻塞其他正常用户的领奖?实际上这里攻击者无法阻止他人,但若此函数是唯一的领奖方式,且攻击者先调用导致失败,他人也无法领奖?不,这里状态未更新,攻击者可以一直尝试,但正常用户也可能被卡?其实这个例子更典型的是:如果合约需要向某地址发送 ETH 作为条件的一部分,而该地址拒绝接收,则整个交易回滚,可能导致某些业务流程永久卡住。

更典型的例子是多签名钱包:一笔交易需要多个签名者批准,其中一个签名者恶意拒绝签名,导致交易永远无法执行。

2.3 权限锁定型(Privilege Lock)

合约的关键角色(如 owner)被设置为无效地址(如零地址),导致所有受保护的函数永久不可用。

典型场景:转移所有权时未检查新地址有效性。

solidity 复制代码
// 漏洞示例:owner 可被设置为零地址
function transferOwnership(address newOwner) public onlyOwner {
    owner = newOwner; // 若 newOwner 为 0x0,所有 onlyOwner 函数永久失效
}

攻击方式:owner 误操作或被恶意引导,将所有权转给零地址。

2.4 存储膨胀型(Storage Bloated)

攻击者通过不断调用函数,使合约的映射或数组无限增长,导致后续操作 Gas 成本过高(例如遍历或读取存储)。

典型场景:允许任何人无成本添加数据到数组。

solidity 复制代码
// 漏洞示例:无限制添加数据
contract Bloat {
    uint[] public data;
    function addData(uint value) public {
        data.push(value); // 任何人都可无限添加
    }
    function processAll() public {
        for (uint i = 0; i < data.length; i++) {
            // 处理数据,Gas 会随数组长度增长
        }
    }
}

攻击方式 :攻击者通过大量交易将数组撑到极大,使 processAll 因 Gas 不足永远无法执行。

2.5 自毁导致合约不可用(Selfdestruct)

合约被 selfdestruct 后,代码和存储被清除,所有功能永久失效。若合约权限控制不当,攻击者可自毁合约。

典型案例 :2017 年 Parity 多签钱包漏洞 。攻击者利用初始化漏洞成为库合约的 owner,然后调用 selfdestruct 销毁了库合约,导致所有依赖该库的钱包无法使用(因为它们通过 delegatecall 调用已销毁的库,调用失败)。


3. 防御措施

攻击类型 防御方法
Gas 耗尽 - 避免无上限循环,使用 分页处理 (每次处理一小部分)。 - 采用 拉取支付(withdraw pattern),让用户自行提取,而非主动推送。
外部调用失败 - 使用 检查返回值 并妥善处理,不要 require(success) 除非业务必须。 - 优先使用 transfer/send (但有 2300 gas 限制,已不推荐),或使用 call 但允许失败并记录 。 - 关键操作可引入 超时/替代方案
权限锁定 - 转移所有权时检查新地址非零 。 - 采用 两步转移 :先提名,再确认。 - 使用 多重签名或时间锁 降低单点风险。
存储膨胀 - 对数据添加操作施加成本 (如支付 Gas 或限制调用频率)。 - 限制数据结构最大长度,或使用 映射替代数组(若无需遍历)。
自毁 - 谨慎使用 selfdestruct,最好完全移除。 - 严格控制权限,避免未授权调用。
通用防御 - 使用重入锁 防止某些 DoS(如重入导致状态不一致)。 - 遵循 最小权限原则 。 - 进行 全面的测试与审计,特别是复杂交互场景。

4. 经典案例回顾

  • Parity 多签钱包事件(2017):库合约被自毁,导致约 50 万 ETH 被锁。
  • GovernorAlpha 投票延迟攻击(Compound):攻击者利用提案机制,使治理系统长时间无法通过新提案。
  • Akropolis 闪贷攻击(2020):攻击者通过重入和 DoS 组合,耗尽池中资金。

5. 总结

智能合约的 DoS 攻击多种多样,但其根源往往是设计时未考虑极端情况或恶意行为 。防御 DoS 需要开发者时刻保持警惕:任何外部依赖都可能是不可靠的,任何循环都可能是无限的,任何权限都可能是脆弱的。遵循安全编码模式(如拉取支付、检查-生效-交互、限制循环)并结合专业审计,是构建抗 DoS 合约的基础。

相关推荐
China_Yanhy4 小时前
入职 Web3 运维日记 · 第 13 日:洗钱风云 —— 链上合规 (KYT) 与多签钱包的权力游戏
运维·web3
EHagSJVNpTY1 天前
直流电机转速、电流双闭环无静差直流调速系统Matlab/Simulink仿真模型及其详细设计说明
智能合约
那年那棵树1 天前
【WebGis】基于WebGis的系统设计与开发(2026.2.11更新)
web3·课程设计
Rockbean1 天前
10分钟智能合约:进阶实战-3.2.2 跨函数重入
web3·智能合约·solidity
Rockbean1 天前
10分钟智能合约:进阶实战-3.2.3 跨合约重入
web3·智能合约·solidity
China_Yanhy1 天前
入职 Web3 运维日记 · 第 12 日:拥堵的跨链桥 —— 消失的 Gas 与“守护者”脚本
运维·web3·php
暴躁小师兄数据学院1 天前
【WEB3.0零基础转行笔记】Go编程篇-第6讲:函数与包
笔记·golang·web3·区块链·智能合约
暴躁小师兄数据学院2 天前
【WEB3.0零基础转行笔记】Solidity编程篇-第2讲:StorageFactory
开发语言·笔记·后端·golang·web3·区块链
China_Yanhy2 天前
入职 Web3 运维日记 · 第 11 日:从 Hex 到 SQL —— 自建链上数据仓库 (Data Warehouse)
运维·sql·web3