拒絕服務攻擊(Denial of Service, DoS)在 Solidity 智能合約中是指攻擊者通過利用合約漏洞,耗盡資源(如 gas、CPU 週期或儲存空間),使合約無法正常運作,從而阻止用戶與合約交互。常見的 DoS 攻擊類型包括:
- Gas 耗盡攻擊:攻擊者創建需要大量 gas 的交易,使合約無法完成執行。
- 重入攻擊:利用合約調用順序漏洞,反覆調用函數以耗盡資源或存取未授權資金。
- 區塊 Gas 限制攻擊:通過提交大量高 gas 消耗的交易,佔用區塊的 gas 限制,阻礙其他合法交易被處理。
這些攻擊的目標是使合約不可用,影響其功能、用戶體驗及相關去中心化應用(dApp)的運作。
範例(存在漏洞的合約)
以下是一個易受 DoS 攻擊的智能合約範例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract Solidity_DOS {
address public king;
uint256 public balance;
function claimThrone() external payable {
require(msg.value > balance, "Need to pay more to become the king");
//If the current king has a malicious fallback function that reverts, it will prevent the new king from claiming the throne, causing a Denial of Service.
(bool sent,) = king.call{value: balance}("");
require(sent, "Failed to send Ether");
balance = msg.value;
king = msg.sender;
}
}
合約功能:
- 這是一個「國王遊戲」合約,允許用戶通過支付比當前
balance
更高的金額(msg.value
)來成為新的「國王」(king
)。 - 當新用戶成功支付後,舊的
king
會收到退款(當前的balance
),然後king
和balance
更新為新用戶的地址和支付金額。
漏洞分析:
- 問題:在
claimThrone
函數中,合約使用king.call{value: balance}("")
向舊的king
發送以太幣。- 如果舊的
king
是一個惡意合約,且其回退函數(receive
或fallback
)故意回滾(例如,通過revert
或無限迴圈耗盡 gas),則call
會失敗,導致require(sent, "Failed to send Ether")
觸發交易回滾。 - 這意味著新用戶無法成為
king
,因為舊king
的惡意行為阻止了合約狀態的更新。
- 如果舊的
- DoS 風險:惡意
king
可以持續阻止任何新用戶更新king
,使合約無法正常運作。
影響
DoS 攻擊可能導致以下後果:
- 合約不可用:用戶無法與合約交互,影響關鍵功能(如資金轉移、狀態更新)。
- 財務損失:在管理資金或資產的 dApp 中,DoS 攻擊可能導致資金無法提取或操作中斷,造成經濟損失。
- 聲譽損害:用戶對合約及其相關平台的信任下降,可能導致用戶流失和商業機會減少。
修復方法
為防止 DoS 攻擊,開發者應採取以下措施:
處理外部調用失敗:
- 使用非阻塞的外部調用處理方式,例如異步處理或將資金轉移延後到獨立函數,確保合約不因單一失敗而癱瘓。
避免高 gas 消耗:
- 謹慎使用
call
(因為它允許接收者執行任意程式碼)以及迴圈或遍歷操作,避免 gas 耗盡。 - 使用
transfer
或send
(有固定 gas 限制)代替call
,減少惡意回退函數的影響。
分散權限:
- 避免將過多權限集中於單一角色(如
king
)。使用多重簽名(Multi-Signature)或分散式權限管理,降低單點故障風險。
狀態更新優先:
- 在執行外部調用(如轉帳)前更新合約狀態,防止因外部調用失敗導致狀態不一致。
修復後的範例合約
以下是修復了 DoS 漏洞的合約:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
contract Solidity_DOS {
address public king;
uint256 public balance;
// Use a safer approach to transfer funds, like transfer, which has a fixed gas stipend.
// This avoids using call and prevents issues with malicious fallback functions.
function claimThrone() external payable {
require(msg.value > balance, "Need to pay more to become the king");
address previousKing = king;
uint256 previousBalance = balance;
// Update the state before transferring Ether to prevent reentrancy issues.
king = msg.sender;
balance = msg.value;
// Use transfer instead of call to ensure the transaction doesn't fail due to a malicious fallback.
payable(previousKing).transfer(previousBalance);
}
}
修復內容:
使用 transfer
代替 call
:
transfer
提供固定 gas 限制(2300 gas),防止接收者的回退函數執行複雜邏輯或故意回滾。- 如果轉帳失敗,交易會自動回滾,確保合約狀態一致。
狀態更新優先:
- 在執行
transfer
前,先更新king
和balance
,避免因轉帳失敗導致狀態未更新(也減少重入風險)。
安全性提升:
- 即使舊
king
的回退函數惡意回滾,transfer
的失敗不會阻止新king
登基,解決 DoS 問題。
總結
拒絕服務攻擊(DoS)通過耗盡資源或利用外部調用失敗使智能合約無法使用。範例中的漏洞合約因使用 call
且未妥善處理惡意回退函數,允許舊 king
阻止新用戶更新狀態,導致合約不可用。修復方法包括使用安全的 transfer
、優先更新狀態、避免高 gas 操作和分散權限。修復後的合約通過 transfer
和狀態優先更新解決了 DoS 風險,確保合約功能正常且用戶可順利交互。開發者應謹慎設計外部調用並進行全面測試,以防止 DoS 攻擊影響合約的可靠性和安全性。