ethereum - 可重入攻击的智能合约良好实践
问题描述
我是一名使用 Solidity 和区块链技术的新手,我正在阅读一些好的实践来改进我的代码。
我有一个关于我不太了解的代码的问题:
来源:https ://github.com/ConsenSys/smart-contract-best-practices/blob/master/docs/known_attacks.md
// INSECURE
mapping (address => uint) private userBalances;
function withdrawBalance() public {
uint amountToWithdraw = userBalances[msg.sender];
require(msg.sender.call.value(amountToWithdraw)()); // At this point, the caller's code is executed, and can call withdrawBalance again
userBalances[msg.sender] = 0;
}
在上面的代码中被认为是不安全的,因为恶意代理可以调用第 2 步所需的次数。我对此的问题是,恶意代理如何调用 misuse this 并多次调用该行代码。我显然在这里遗漏了一些东西。
解决方案
这被称为重入攻击。
这是不安全的,因为只有在处理完提款后用户的余额才设置为 0。此外,提款是通过使用 evm 的 CALL 操作码处理的,该操作码将控制权传递给接收地址。
如果接收地址是一个合约,它可以使用回退功能劫持这个转账。在这个后备函数中,它可以检查发送合约的余额是否超过转移的金额。如果是,它将withdrawBalance
再次调用,直到提款合约的余额被耗尽。
一个简单的攻击者合约可能看起来像:
contract Attacker {
function startattack() {
victim.withdrawBalance();
}
function() payable {
if (victim.balance > msg.value) {
victim.withdrawBalance();
}
}
}
通过调用startattack
,您发起提款。执行该require(msg.sender.call.value(amountToWithdraw)());
行时,它会运行回退函数中的代码。此时,msg.value
是userBalances[msg.sender]
。攻击者可以检查受害者的合约是否还有比 更多的可用以太币userBalances[msg.sender]
,然后再次运行提款(这将导致此过程循环,直到余额低于userBalances[msg.sender]
)。
这可以通过将行的顺序切换为:
function withdrawBalance() public {
uint amountToWithdraw = userBalances[msg.sender];
userBalances[msg.sender] = 0;
require(msg.sender.call.value(amountToWithdraw)());
}
现在,即使攻击者withdrawBalance
再次调用,用户的余额已经被设置为 0,并且无法进一步提款。
推荐阅读
- python - OSError: [Errno 22] 无效参数 tryign 克隆到 github 页面
- javascript - React 在挂载时呈现一个空数组项,我该如何避免这种情况?
- c++ - 访问抽象类模板的受保护数据成员无法编译
- python - add.Constrs Gurobi 内的嵌套循环
- adobe - 将受 adobe 保护的 cxt 和 dxr 转换为常规视频
- javascript - 使用 HtmlMediaElement.captureStream() 从视频中获取音频
- mysql - 如何复制新数据但跳过 MySQL 中 2 个表中的旧数据
- reactjs - React Router DOM - Webpack - 加载资源失败:服务器响应状态为 404(未找到)
- mql4 - 如何删除字符串的某个部分(如果存在)?
- dart - 了解int之间的区别?和 int(或 num?和 num)