当前位置: 首页 > news >正文

区块链智能合约( solidity) 安全编程

引言:本文由天玄链开源开发者提供,欢迎报名公益天玄链训练营

https://blockchain.163.com/trainingCamp

一、重入和竞态

重入和竞态在solidity 编程安全中会多次提及,历史上也造成了重大的损失。

1.1 问题分析

竞态的描述不严格,竞态是在并发编程中的概念。而以太坊智能合约是单线程运行的。不存在并发。它的问题来自于重入。

我们在实现智能合约无法做到可重入的,所以就要添加重入的保护。一般情况下,重入问题来自于fallback 或者 recevie 方法,因为以太坊转账是最常用的功能。

当然如果调用了其他不安全的合约一样有这个问题。

1.2 可重入保护

一般的解决方案,“检查-生效-交互”,这个方法是现在常用的,也是推荐使用的。

function untrustedWithdraw(address recipient) public {

    uint amountToWithdraw = userBalances[recipient];

    rewardsForA[recipient] = 0;

    if (!(recipient.call.value(amountToWithdraw)())) { throw; }

}

  

function untrustedGetFirstWithdrawalBonus(address recipient) public {

    if (claimedBonus[recipient]) { throw; } // 检查

    claimedBonus[recipient] = true;//生效

    rewardsForA[recipient] += 100;

    untrustedWithdraw(recipient); // 交互

}

另外,既然它表现的像多线程并发。还有一种解决方案,就是加锁。openzeppelin 中 ReentrancyGuard合约中的nonReentrant modifier也是阻止重入的一种方法。

但是需要注意的是,这个modifier  只能对单个方法起作用,需要在所有状态相关方法上添加才能防止交叉重入。

综上,“检查-生效-交互” 推荐使用来防止重入。

 1.3 显示标示untrust

在调用不安全外部合约的方法时,需要将该方法标示为untrust。同时,调用该untrust 的方法也应该是untrust。

对待untrust 方法规范是,首先处理完内部状态,最后调用untrust 方法。向外部地址转账,显然也是调用了外部合约方法,属于untrust 调用

二、安全转账

在uniswap中有很多转账的范例,比如下面的代码,将常用的ERC20 转账,注意,在safeTransferETH 中,它使用了to.call 而不是 to.transfer,因为而.send 或者 .transfer 是发送固定gas,

 solidity 升级后,一些操作码的gas 可能有变化。这就导致之前的fallback 方法可能会失败。对建立在uniswap上的生态造成影响。

但是直接使用.call 有很多风险,比如要校验返回值,再比如重入攻击问题。所以尽量使用成熟的范式,是增强代码安全的有效手段。这里可能引入的重入风险,我们后面再单独分析。

function safeTransfer(

    address token,

    address to,

    uint256 value

) internal {

    // bytes4(keccak256(bytes('transfer(address,uint256)')));

    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));

    require(

        success && (data.length == 0 || abi.decode(data, (bool))),

        'TransferHelper::safeTransfer: transfer failed'

    );

}

function safeTransferFrom(

    address token,

    address from,

    address to,

    uint256 value

) internal {

    // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));

    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));

    require(

        success && (data.length == 0 || abi.decode(data, (bool))),

        'TransferHelper::transferFrom: transferFrom failed'

    );

}

function safeTransferETH(address to, uint256 value) internal {

    (bool success, ) = to.call{value: value}(new bytes(0));

    require(success, 'TransferHelper::safeTransferETH: ETH transfer failed');

}

三、以太发送方法

3.1 常用方法

常用的两种方法,send, transfer

send与transfer对比简析

相同之处

(1)均是向address发送ETH(以Wei做单位)

(2)发送的同时传输2300gas(gas数量很少,只允许接收方合约执行最简单的操作)

不同之处

(1)send执行失败返回false,transfer执行失败则会throw。这也就意味着使用send时一定要判断是否执行成功。

推荐

(1)默认情况下最好使用transfer(因为内置了执行失败的处理)

安全推荐是transfer,相信大家都知道。那么为什么transfer 更好,在于它是更高级的方法,封装了校验返回值,并抛出异常。

3.2 引申一下

我想引申一点是,在我们写方法的时候,当校验时,应该使用require 而不是 if,使用if 使得方法没有完成必要逻辑,而交易成功。这其实有隐患的。

除非你非常清楚这个差别,并认为只有if 才能满足函数需求。

比如有一个编程模式做频率检查,模式设计推荐的是

modifier enabledEvery(uint t) {   

  if (now >= enabledAt) {

    enabledAt = now + t;

    _;

  }

}

其实这个设计就有我上面说的问题,虽然达到了限制频率的目的,但是整个交易时成功的。正常使用就会有很多困惑,一旦被集成到其他合约,就会造成攻击的隐患。

建议是使用require,一旦失败,交易会revert,而且原因清晰。

modifier enabledEvery(uint t) {

  require(now >= enabledAt,"too freq, wait a minute");

  enabledAt = now + t;

  _;

3.3 转账成功的条件

如果对方是合约地址,那么它需要具备receive 方法,或者payable 的fallback 方法。如果两者都没有则转账失败。但是从另一面说,没有这两个方法不代表不能收取以太。

这还是要从概念上区分一下。

这就引申出两个问题,

3.3.1 合约为用户转账

要考虑到失败的可能。尽量使用pull 而不是push 方法。也就是让用用户发起withdraw收取以太,而不是合约dispatch。 

3.3.2 合约需要收帐的。

即便没有设置payable 的fallback,和 receive 方法,你依然不能阻止绕过收款方法,直接转账给合约,使得合约balance 跟合约内账本对不上。

所以,业务逻辑要使用自己的账本。

3.3.3 非标准ERC20 的影响

业务逻辑建立在自己的账本上就够了吗?对于有些token 虽然符合ERC 标准,但是具体实现却又做了一些改动,比如有个项目做了转账收手续费的操作,

在转账过程中收取。也就是实际到账比交易执行的要少,如果只关注交易是否成功,然后记账是造成漏洞,所以这种情况,还需要去查balance 的变化。

以上是在转账过程需要注意的,不仅要合约记录账本,还需要核对balance,以保证业务逻辑没有漏洞。

当然如果业务确信没有此类问题。可以简化,比如只涉及USDT 的入账,那么可以不考虑3.3.3。

四、慎用tx.origin

永远不要 tx.origin 用于授权,另一个合约可以有一个方法来调用你的合约(例如,用户有一些资金)并且你的合约将授权该交易,因为你的地址位于tx.origin.

contract MyContract {address owner;function MyContract() public {owner = msg.sender;}function sendTo(address receiver, uint amount) public {require(tx.origin == owner);(bool success, ) = receiver.call.value(amount)("");require(success);}}contract AttackingContract {MyContract myContract;address attacker;function AttackingContract(address myContractAddress) public {myContract = MyContract(myContractAddress);attacker = msg.sender;}function() public {myContract.sendTo(attacker, msg.sender.balance);}}

这是最常见的错误。

如果我们用tx.origin 能用来干嘛?我们先引申一下。EOA 的概念。

4.1 如何判断一个账户是EOA 账户而不是contract。

一种做法是如下,很多业务是这么写的。但其实是有问题。某些情况会失效,比如constructor中。

/** @dev Modifier requiring sender to be EOA.  This check could be bypassed by a malicious

 *  contract via initcode, but it takes care of the user error we want to avoid.

 */

modifier onlyEOA() {

    // Used to stop deposits from contracts (avoid accidentally lost tokens)

    require(!Address.isContract(msg.sender), "Account not EOA");

    _;

}

那么如何判断EOA 呢,这里就用到了tx.origin. 

    modifier onlyEOA() {

        // Used to stop deposits from contracts (avoid accidentally lost tokens)

        require(tx.origin == msg.sender, "Account not EOA");

        _;

    }

  

相关文章:

区块链智能合约( solidity) 安全编程

引言:本文由天玄链开源开发者提供,欢迎报名公益天玄链训练营 https://blockchain.163.com/trainingCamp 一、重入和竞态 重入和竞态在solidity 编程安全中会多次提及,历史上也造成了重大的损失。 1.1 问题分析 竞态的描述不严格&#xf…...

GUNS搭建

一、准备工作 源码下载: 链接: https://pan.baidu.com/s/1bJZzAzGJRt-NxtIQ82KlBw 提取码: criq 官方文档 二、导入代码 1、导入后端IDE 导入完成需要,需要修改yml文件中的数据库配置,改成自己的。 2、导入前端IDE 我是用npm安装的yarn npm…...

【ETCD】【源码阅读】stepWithWaitOption方法解析

在分布式系统中,ETCD 作为一个强一致性、高可用的 key-value 存储系统,广泛应用于服务发现、配置管理等场景。ETCD 在内部采用了 Raft 协议来保证集群的一致性,而日志预提案(log proposal)是 Raft 协议中至关重要的一部…...

redis 怎么样查看list

在 Redis 中,可以通过以下方法查看列表的内容或属性: 1. 查看列表中的所有元素 使用 LRANGE 命令: LRANGE key start endkey 是列表的名称。start 是起始索引,0 表示第一个元素。end 是结束索引,-1 表示最后一个元素…...

E: 无法获取 dpkg 前端锁 (/var/lib/dpkg/lock-frontend),是否有其他进程正占用它?

我们在使用Ubuntu系统时经常性使用sudo apt install命令安装所需要的软件库,偶尔会出现如下问题: E: 无法获得锁 /var/lib/dpkg/lock-frontend - open (11: 资源暂时不可用) E: 无法获取 dpkg 前端锁 (/var/lib/dpkg/lock-frontend),是否有其…...

创建型设计模式

一、设计模式介绍 1.设计模式是什么 设计模式是指在软件开发中,经过验证的,用于解决在特定环境下,重复出现的,特定问题的解决方案; 2.设计模式怎么来的? 满足设计原则后,慢慢迭代出来的。 3.设…...

仿iOS日历、飞书日历、Google日历的日模式

仿iOS日历、飞书日历、Google日历的日模式,24H内事件可自由上下拖动、自由拉伸。 以下是效果图: 具体实现比较简单,代码如下: import android.content.Context; import android.graphics.Canvas; import android.graphics.Color;…...

vuedraggable

官方文档:https://www.npmjs.com/package/vuedraggable 中文文档:http://www.itxst.com/vue-draggable/tutorial.html 案例下载地址: https://github.com/SortableJS/Vue.Draggable.git vuedraggablehttps://sortablejs.github.io/Vue.Dr…...

新手从事直播软件源码开发搭建经验与技巧

如果从YY或六间房的PC秀场直播间系统软件算起,直播软件已经在国内风云了至少10年了,站在用户角度提到直播系统大家基本都知道核心功能有开直播刷礼物等,那么如果站在直播软件源码开发搭建的技术角度去看呢?是不是要从需求调研分析…...

相机不动,机构动作----Hands Eyes

最近在研究 手眼标定,发现大家都需付费,搞啥子,说好的开源。。。 以相机在上固定不动,机械手为 EPSON_Robot 为例,详细的一步一步实例操作指引 EPSON_Robot 的192.168.0.1 2004 Server 详细操作步骤 1. 启动程序 运…...

Scala的导入

//导入 //(1) 创建包:在src上右键,新建软件包 //(2)填写包名:小写 //(3)在包上右键,创建类。自动加入包名 //(4)导入。import 包名.类名 //导入多个类 //import jh.yuanlixueyuan.bigdata.scala03.{A,B,C} //导入包下的所有的类 /…...

vue2中父子组件传值案例总结

在 Vue 2 中&#xff0c;父子组件之间的传值是通过 props 和事件来实现的。下面是详细的解释和总结&#xff1a; 1. 父组件向子组件传值 父组件可以通过 props 向子组件传递数据。以下是一个简单的示例&#xff1a; 父组件 (Parent.vue) <template><div><h1…...

功能篇:springboot中实现文件导出

### Spring Boot 中实现文件导出功能 #### 概述 在现代Web应用程序中&#xff0c;文件导出是一个常见的需求&#xff0c;允许用户将数据以特定格式&#xff08;如CSV、Excel、PDF等&#xff09;下载到本地。本文将详细介绍如何使用Spring Boot实现文件导出功能&#xff0c;并…...

Redis客户端(Jedis、RedisTemplate、Redisson)

1. 简介 Redis作为一个当下很火热的非关系型数据库&#xff0c;Java从业人员基本都离不开对Redis的使用。在Java程序中该数据库&#xff0c;需要借助于市面上的开源客户端&#xff0c;如Jedis、Spring Data Redis、Redisson&#xff0c;它们可以作为操作Redis非关系型数据库的桥…...

Mybatis中SQL的执行过程

文章目录 Mybatis 框架SQL执行过程数据库操作映射方式SQL的执行过程- SQL解析- SQL参数映射- SQL预编译- SQL执行- 结果映射- 事务处理- 缓存处理- 日志记录与监控 扩展#与$的区别- $ 符号- # 符号总结示例 Mybatis SQL分类- 动态 SQL- 静态 SQL静态SQL和动态SQL选择${}、#{}与…...

【数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】

目录&#x1f60b; 任务描述 相关知识 测试说明 我的通关代码: 测试结果&#xff1a; 任务描述 本关任务&#xff1a;编写一个程序实现顺序栈的基本运算。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a; 初始化栈、销毁栈、判断栈是否为空、进栈、出栈、取…...

【论文阅读】PRIS: Practical robust invertible network for image steganography

内容简介 论文标题&#xff1a;PRIS: Practical robust invertible network for image steganography 作者&#xff1a;Hang Yang, Yitian Xu∗, Xuhua Liu∗, Xiaodong Ma∗ 发表时间&#xff1a;2024年4月11日 Engineering Applications of Artificial Intelligence 关键…...

在Linux桌面系统普及化方面的一些建议

在推动Linux桌面系统普及化的过程中&#xff0c;可以考虑以下几个方案和策略&#xff1a; 用户友好性改进&#xff1a; 界面设计&#xff1a;提升用户界面的美观性和易用性&#xff0c;使其更接近或超越主流操作系统的用户体验。软件兼容性&#xff1a;确保常用软件&#xff08…...

LLM - 多模态大模型的开源评估工具 VLMEvalKit 部署与测试 教程

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/144353087 免责声明&#xff1a;本文来源于个人知识与公开资料&#xff0c;仅用于学术交流&#xff0c;欢迎讨论&#xff0c;不支持转载。 VLMEva…...

数据结构(Queue队列)

前言&#xff1a; 在计算机科学中&#xff0c;数据结构是构建高效算法和程序的基础&#xff0c;而队列&#xff08;Queue&#xff09;作为一种经典的线性数据结构&#xff0c;具有重要的地位。与栈&#xff08;Stack&#xff09;不同&#xff0c;队列遵循“先进先出”&#xf…...

Qt 图形框架下图形拖动后位置跳动问题

在使用Qt 的图形框架QGraphicsScene&#xff0c;QGraphicsView实现图形显示时。遇到一个很棘手的BUG。 使用的图形是自定义的QGraphicsObject的子类。 现象是将图形添加到画布上之后&#xff0c;用鼠标拖动图形&#xff0c;图形能正常改变位置&#xff0c;当再次用鼠标点击图…...

【Linux篇】走进Linux — 开启开源操作系统之旅

文章目录 初识Linux一.Linux的起源与发展二.Linux的特点三.Linux的应用四.Linux的发行版本 Linux环境搭建一.Linux环境的搭建方式二.购买云服务器三.使用XShell远程登陆到Linux 初识Linux 一.Linux的起源与发展 1.初始动机&#xff1a; Linux是一个功能强大的开源操作系统&am…...

如何利用DBeaver配置连接MongoDB和人大金仓数据库

最近根据国产化要求&#xff0c;需要使用国产数据库&#xff0c;但习惯使用DBeaver连接各种成熟的商业或开源数据库。因此&#xff0c;就想着如何继续基于该工具&#xff0c;连接MongoDB和人大金仓数据库&#xff0c;查了半天很多地方说法不统一&#xff0c;所以自己就简单整理…...

Android 车载虚拟化底层技术-Kernel 5.10 -Android12(multi-cards)技术实现

详细代码实现见 Android Display Graphics系列文章-汇总​​​​​​Android Display Graphics系列文章-汇总 Android Display Graphics系列文章-汇总 Android Display Graphics系列文章-汇总 本文主要包括部分&#xff1a; 一、Android12的Kernel 5.10版本 1.1 Kernel 5…...

Qt之点击鼠标右键创建菜单栏使用(六)

Qt开发 系列文章 - menu&#xff08;六&#xff09; 目录 前言 一、示例演示 二、菜单栏 1.MenuBar 2.Menu 总结 前言 QMainWindow是一个为用户提供主窗口程序的类&#xff0c;包含一个菜单栏&#xff08;menubar&#xff09;、多个工具栏(toolbars)、一个状态栏(status…...

开发一套SDK 第一弹

自动安装依赖包 添加条件使能 #ex: filetypesh bash_ls 识别 达到预期,多个硬件环境 等待文件文件系统挂在完成 或者创建 /sys/class/ 属性文件灌入配置操作 AI 提供的 netlink 调试方法,也是目前主流调用方法,socket yyds #include <linux/module.h> #include <linux…...

sftp+sshpass

实现场景&#xff0c;要求客户端定时将本地的日志文件传输到服务器。 工作环境ubuntu&#xff0c;注意不通操作系统的版本不通&#xff0c;依赖的工具的版本也有所不同 实现目标需要客户端满足安装工具&#xff1a; 1、下载安装sshpass ---安装命令&#xff1a;sudo apt-ge…...

【机器学习与数据挖掘实战】案例01:基于支持向量回归的市财政收入分析

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈机器学习与数据挖掘实战 ⌋ ⌋ ⌋ 机器学习是人工智能的一个分支&#xff0c;专注于让计算机系统通过数据学习和改进。它利用统计和计算方法&#xff0c;使模型能够从数据中自动提取特征并做出预测或决策。数据挖掘则是从大型数…...

Idea实现定时任务

定时任务 什么是定时任务&#xff1f; 可以自动在项目中根据设定的时长定期执行对应的操作 实现方式 Spring 3.0 版本之后自带定时任务&#xff0c;提供了EnableScheduling注解和Scheduled注解来实现定时任务功能。 使用SpringBoot创建定时任务非常简单&#xff0c;目前主要…...

Linux 安装NFS共享文件夹

程序默认使用2049端口&#xff0c;如果被占用需要修改端口104设置为服务端 122设置为客户端 一、在线安装&#xff08;服务端和客户端执行&#xff09; yum install nfs-utils rpcbind -y二、配置启动参数&#xff08;服务端执行&#xff09; 104服务器/mnt路径下创建shareda…...