Solidity 合约安全,常见漏洞(第三篇)
Solidity 合约安全,常见漏洞(第三篇)
ERC20 代币问题
如果你只处理受信任的 ERC20 代币,这些问题大多不适用。然而,当与任意的或部分不受信任的 ERC20 代币交互时,就有一些需要注意的地方。
ERC20:转账扣费
当与不信任的代币打交道时,你不应该认为你的余额一定会增加那么多。一个 ERC20 代币有可能这样实现它的转账函数,如下所示:
contract ERC20 {// internally called by transfer() and transferFrom()// balance and approval checks happen in the callerfunction _transfer(address from, address to, uint256 amount) internal returns (bool) {fee = amount * 100 / 99;balanceOf[from] -= to;balanceOf[to] += (amount - fee);balanceOf[TREASURY] += fee;emit Transfer(msg.sender, to, (amount - fee));return true;}
}
这种代币对每笔交易都会征收 1%的税。因此,如果一个智能合约与该代币进行如下交互,我们将得到意想不到的回退或资产被盗。
contract Stake {mapping(address => uint256) public balancesInContract;function stake(uint256 amount) public {token.transferFrom(msg.sender, address(this), amount);balancesInContract[msg.sender] += amount; // 这是错误的}function unstake() public {uint256 toSend = balancesInContract[msg.sender];delete balancesInContract[msg.sender];// this could revert because toSend is 1% greater than// the amount in the contract. Otherwise, 1% will be "stolen"// from other depositors.token.transfer(msg.sender, toSend);}
}
ERC20: rebase 的代币
Rebasing 代币由 Olympus DAO 的 sOhm 代币 和 Ampleforth 的 AMPL 代币所推广。Coingecko 维护了一个 Rebasing ERC20 代币的列表。
当一个代币回溯时,总发行量会发生变化,每个人的余额会根据回溯的方向而增加或减少。
在处理 rebase 代币时,以下代码可能会被破坏:
contract WillBreak {mapping(address => uint256) public balanceHeld;IERC20 private rebasingTokenfunction deposit(uint256 amount) external {balanceHeld[msg.sender] = amount;rebasingToken.transferFrom(msg.sender, address(this), amount);}function withdraw() external {amount = balanceHeld[msg.sender];delete balanceHeld[msg.sender];// 错误, amount 也许会超出转出范围rebasingToken.transfer(msg.sender, amount);}
}
许多合约的解决方案是简单地不允许 rebase 代币。然而,我们可以修改上面的代码,在将账户余额转给接受者之前检查 balanceOf(address(this))。那么,即使余额发生变化,它仍然可以工作。
ERC20: ERC777 在 ERC20 上的包裹
ERC20,如果按照标准实现,ERC20 代币没有转账钩子(hook),因此 transfer 和 transferFrom 不会有重入问题。
带有转账钩子的代币有应用优势,这就是为什么所有的 NFT 标准都实现了它们,以及为什么 ERC777 被最终确定。然而,这已经引起了足够的混乱,以至于 Openzeppelin 废止了 ERC777 库。
如果你只想让你的协议与那些行为像 ERC20 代币但有转账 hook 的代币兼容,那么这只是一个简单的问题,把 transfer 和 transferFrom 函数当作它们会向接收者进行一个函数调用即可。
这种 ERC777 的重入发生在 Uniswap 身上(如果你好奇,Openzeppelin 在这里记录了这个漏洞)。
ERC20: 不是所有的 ERC20 代币转账都会返回 true
ERC20 规范规定,ERC20 代币在转账成功时必须返回 true。因为大多数 ERC20 的实现不可能失败,除非授权不足或转账的金额太多,大多数开发者已经习惯于忽略 ERC20 代币的返回值,并假设一个失败的 trasfer 将被回退。
坦率地说,如果你只与一个你知道其行为的受信任的 ERC20 代币打交道,这并不重要。但在处理任意的 ERC20 代币时,必须考虑到这种行为上的差异。
在许多合约中都有一个隐含的期望,即失败的转账应该总是回退,而不是返回错误,因为大多数 ERC20 代币没有返回错误的机制,所以这导致了很多混乱。
使这个问题更加复杂的是,一些 ERC20 代币并不遵循返回 true 的协议,特别是 Tether。一些代币在转账失败后会回退,这将导致回退的结果冒泡到调用者。因此,一些库包裹了 ERC20 代币的转账调用,以回退恢复并返回一个布尔值。下面是一些实现方法:
参考:Openzeppelin SafeTransfer 及 Solady SafeTransfer (大大地提高了 Gas 效率)
ERC20: 地址投毒
这不是一个智能合约的漏洞,但为了完整起见,我们在这里提到它。
转账零代币是 ERC20 规范所允许的。这可能会导致前端应用程序的混乱,并可能欺骗用户,让他们错误的以为他们最近将代币发送给了某地址。Metamask在这个线程中有更多关于这个问题的内容。
ERC20: 查看代码,规避跑路
(在 web3 术语中,“rugged"意味着’'跑路”, 直译是"从你脚下拉出地毯" 。)
没有什么能阻止有人在 ERC20 代币上添加函数,让他们随意创建、转账和销毁代币–或自毁或升级。所以从根本上说,ERC20 代币的 “无需信任” 程度是有限制的。
借贷协议中的逻辑错误
当考虑到基于 DeFi 协议的借贷如何被破坏时,考虑在软件层面传播的 bug 并影响商业逻辑层面是很有帮助的。形成和完成一个债券合约有很多步骤。这里有一些需要考虑的攻击向量。
贷款人损失的方式
- 使到期本金减少(可能为零)而不进行任何支付的漏洞。
- 当贷款没有偿还或抵押物降到阈值以下时,买方的抵押物不能被清算。
- 如果协议有一个转移债务所有权的机制,这可能是一个从贷款人那里偷取债券的方式。
- 贷款本金或付款的到期日被不适当地移到以后的日期。
借款人损失的方式
- 偿还本金时没有减少本金债务的 bug。
- 一个 bug 或 gas 攻击使用户无法进行支付。
- 本金或利率被非法提高。
- 预言机的操纵导致抵押物贬值。
- 贷款本金或付款的到期日被不适当地移到一个较早的日期。
如果抵押品从协议中被抽走,那么贷款人和借款人都会损失,因为借款人没有动力去偿还贷款,而借款人则会损失本金。
正如上面所看到的,DeFi 协议被 "黑 "的范围比从协议中抽走一堆钱(通常成为新闻的那类事件)要多得多。
抵押(staking)协议中的漏洞
成为新闻的那种黑客是抵押协议被黑掉数百万美元,但这并不是唯一要面对的问题,抵押协议可能面临的问题有:
- 奖励能否延迟支付,或过早地被索取?
- 奖励能否被不适当地减少或增加?在更糟糕的情况下,能否阻止用户获得任何奖励?
- 人们能否索取不属于他们的本金或奖励,在最坏的情况下,会耗尽协议所有资金?
- 存放的资产会不会被卡在协议中(部分或全部),或被不适当地延迟提取?
- 相反,如果质押需要时间承诺,用户是否可以在承诺时间之前提取?
- 如果支付的是不同的资产或货币,其价值是否可以在相关的智能合约范围内被操纵?如果协议 mint 自己的代币来奖励流动性提供者或质押者,这一点是相关的。
- 如果存在预期和披露出的本金损失的风险因素,这种风险是否可以被不适当地操纵?
- 协议的关键参数是否有管理、中心化或治理风险?
需要关注的关键是代码中涉及 "资金退出 "部分的代码。
还有一个 "资金入口 "的漏洞也要寻找。
- 有权参与协议中的资产抵押的用户能否被不适当地阻止?
用户收到的奖励有一个隐含的风险回报和一个预期的资金时间价值。明确这些假设是什么,以及协议会怎样偏离预期是很有帮助的。
未检查的返回值
有两种方法来调用外部智能合约:1)用接口定义调用函数;2)使用.call 方法。如下图所示:
contract A {uint256 public x;function setx(uint256 _x) external {require(_x > 10, "x must be bigger than 10");x = _x;}
}interface IA {function setx(uint256 _x) external;
}contract B {function setXV1(IA a, uint256 _x) external {a.setx(_x);}function setXV2(address a, uint256 _x) external {(bool success, ) =a.call(abi.encodeWithSignature("setx(uint256)", _x));// success is not checked!}
}
在合约 B 中,如果 _x 小于 10,setXV2 会默默地失败。当一个函数通过.call 方法被调用时,被调用者可以回退,但父函数不会回退。必须检查返回成功的值,并且代码行为必须相应地分支。
msg.value 在一个循环中
在循环中使用 msg.value 是很危险的,因为这可能会让发起者 重复使用 msg.value。
这种情况可能会出现在 payable 的 multicalls 中。Multicalls 使用户能够提交一个交易列表,以避免重复支付 21,000 的 Gas 交易费。然而,msg.value 在通过函数循环执行时被 “重复使用”,有可能使用户双花。
这就是Opyn Hack的根本原因。
私有变量
私有变量在区块链上仍然是可见的,所以敏感信息不应该被存储在那里。如果它们不能被访问,验证者如何能够处理取决于其值的交易?私有变量不能从外部的 Solidity 合约中读取,但它们可以使用以太坊客户端在链外读取。
要读取一个变量,你需要知道它的存储槽。在下面的例子中,myPrivateVar 的存储槽是 0。
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract PrivateVarExample {uint256 private myPrivateVar;constructor(uint256 _initialValue) {myPrivateVar = _initialValue;}
}
下面是读取已部署的智能合约的私有变量的 javascript 代码
const Web3 = require("web3");
const PRIVATE_VAR_EXAMPLE_ADDRESS = "0x123..."; // Replace with your contract addressasync function readPrivateVar() {const web3 = new Web3("http://localhost:8545"); // Replace with your provider's URL// Read storage slot 0 (where 'myPrivateVar' is stored)const storageSlot = 0;const privateVarValue = await web3.eth.getStorageAt(PRIVATE_VAR_EXAMPLE_ADDRESS,storageSlot);console.log("Value of private variable 'myPrivateVar':",web3.utils.hexToNumberString(privateVarValue));
}readPrivateVar();
不安全的代理调用
委托调用(Delegatecall)不应该被用于不受信任的合约,因为它把所有的控制权都交给了委托接受者。在这个例子中,不受信任的合约偷走了合约中所有的以太币。
contract UntrustedDelegateCall {constructor() payable {require(msg.value == 1 ether);}function doDelegateCall(address _delegate, bytes calldata data) public {(bool ok, ) = _delegate.delegatecall(data);require(ok, "delegatecall failed");}}contract StealEther {function steal() public {// you could also selfdestruct here// if you really wanted to be mean(bool ok,) =tx.origin.call{value: address(this).balance}("");require(ok);}function attack(address victim) public {UntrustedDelegateCall(victim).doDelegateCall(address(this),abi.encodeWithSignature("steal()"));}
}
升级与代理有关的 bug
我们无法在一个章节中对这个话题进行公正的解释。大多数升级错误通常可以通过使用 Openzeppelin 的hardhat 插件和阅读它所保护的问题来避免出错。
作为一个快速的总结,以下是与智能合约升级有关的问题:
- 自毁(self-destruct)和委托调用(delegatecall)不应该在执行合约中使用。
- 必须注意在升级过程中,存储变量不能相互覆盖
- 在执行合约中应避免调用外部库,因为不可能预测它们会如何影响存储访问。
- 部署者决不能忽视调用初始化函数
- 在基类合约中没有包括间隙(gap)变量,以防止在基类合约中加入新的变量时发生存储碰撞(这由 hardhat 插件自动处理)。
- 不可变(immutable)变量中的值在升级时不会被保留
- 非常不鼓励在构造函数中做任何事情,因为未来的升级必须执行相同的构造函数逻辑以保持兼容性。
相关文章:
Solidity 合约安全,常见漏洞(第三篇)
Solidity 合约安全,常见漏洞(第三篇) ERC20 代币问题 如果你只处理受信任的 ERC20 代币,这些问题大多不适用。然而,当与任意的或部分不受信任的 ERC20 代币交互时,就有一些需要注意的地方。 ERC20&#…...
Linux安装Redis数据库,无需公网IP实现远程连接
文章目录 1. Linux(centos8)安装redis数据库2. 配置redis数据库3. 内网穿透3.1 安装cpolar内网穿透3.2 创建隧道映射本地端口 4. 配置固定TCP端口地址4.1 保留一个固定tcp地址4.2 配置固定TCP地址4.3 使用固定的tcp地址连接 Redis作为一款高速缓存的key value键值对的数据库,在…...
智慧政务,长远布局——AIGC引领,加速推进数字化政府建设
在人工智能、虚拟现实等领域迅猛发展且日益成熟的背景下,AI行业正迈向蓬勃发展的全新阶段,市场规模持续扩张。与此同时,数字服务也正在蓬勃兴起,新一代信息技术为数字政府构建了坚实支撑,重塑了政务信息化管理、业务架…...
中央处理器(CPU):组成、指令周期、数据通路、控制方式、控制器、指令流水线,补充(多处理器系统、硬件多线程)
中央处理器(CPU,Central Processing Unit),计算机控制和运算的核心,是信息处理和程序运行的执行单元。 CPU主要功能:处理指令、执行操作、控制时间、处理中断、处理数据。 其中,处理指令、执行…...
开源微服务如何选型?Spring Cloud、Dubbo、gRPC、Istio 详细对比
作者:刘军 不论您是一名开发者、架构师、CTO, 如果您曾深度参与在微服务开发中,那么相信您一定有过开源微服务框架或体系选型的疑问:Apache Dubbo、Spring Cloud、gRPC 以及 Service Mesh 体系产品如 Istio,到底应该选…...
Nginx的HTTPS部署与安全性能优化
Nginx作为一款高性能的Web服务器和反向代理服务器,被广泛用于应用部署和负载均衡。在安全环保意识的逐渐提高下,HTTPS也成为现代Web应用中必不可少的一环。本篇文章将重点介绍Nginx的HTTPS部署和安全性能优化。 一、Nginx的HTTPS部署 证书申请 首先需要去…...
5.8. Trusted Board Boot
5.8. Trusted Board Boot启动 Trusted Board Boot(TBB) 功能通过验证所有固件镜像(包括正常世界引导加载程序)来防止恶意固件在平台上运行。它通过使用公钥加密标准 (PKCS) 建立信任链来实现这一点。 本文介绍了可信固件-A (TF-A) TBB 的设计,它是Trusted Board Boot(TBBR…...
微信小程序——van-field中的left-icon属性自定义
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...
一文学会lua脚本
文章目录 0.前言背景应用 1. 学习大纲1. 学习基本语法:2. 理解函数和模块:3. 深入数据结构:4. 高级特性和技巧:5. 实践项目: 2. Lua脚本2.1 学习基本语法2.2 理解函数和模块2.3 深入数据结构2.4 高级特性和技巧 3. 高级…...
学习JAVA打卡第四十二天
正则表达式及字符串的替换与分解 ⑴正则表达式 正则表达式是string对象的字符序列,该字符序列中含有具有特殊意义的字符,这些特殊字符称作正则表达式的元字符。 注:由于“ ”代表任何一个字符,所以在正则表达式中如果想使用普…...
2023-8-25食物链
题目链接:食物链 #include <iostream>using namespace std;const int N 50010;int n, m; int p[N], d[N];int find(int x) {if(p[x] ! x){int t find(p[x]);d[x] d[p[x]];p[x] t;}return p[x]; }int main() {cin >> n >> m;for(int i 1; i…...
为什么要使用IP地址进行定位
IP地址定位是一种以互联网协议地址(IP地址)为基础的技术,它能够准确地确定一个设备在互联网上的位置。这种技术的应用范围非常广泛,从个人用户到企业机构甚至是国家安全和网络安全等领域都需要使用IP地址定位。 首先,I…...
CSS概念
1、CSS与HTML结合方式 1.1 第一种方式 内联/行内样式 就是在我们的HTML标签上通过style属性来引用CSS代码。 优点:简单方便 ; 缺点:只能对一个标签进行修饰。 1.2 第二种方式 内部样式 我们通过<style>标签来声明我们的CSS. 通常<style>标签我们推荐写在…...
淘宝API技术解析,实现关键词搜索淘宝商品(商品详情接口等)
淘宝提供了开放平台接口(API)来实现按图搜索淘宝商品的功能。您可以通过以下步骤来实现: 获取开放平台的访问权限:首先,您需要在淘宝开放平台创建一个应用,获取访问淘宝API的权限。具体的申请步骤和要求可以…...
Redis 7 教程 数据类型 基础篇
🌹 引导 Commands | Redishttps://redis.io/commands/Redis命令中心(Redis commands) -- Redis中国用户组(CRUG)Redis命令大全,显示全部已知的redis命令,redis集群相关命令,近期也会翻译过来,Redis命令参考,也可以直接输入命令进行命令检索。...
-bash: tree: command not found 的解决方法
在学习git操作时发现使用命令tree .git时显示错误 在网上查阅资料后,发现可能是没有安装生成tree的应用,所以我们使用命令安装应用即可 sudo yum install -y tree像这样就是安装成功了 我们再来试试 问题解决了,成功显示出树形结构...
SPI总线协议
简述 SPI协议是一种芯片与芯片之间的通讯,全称是Serial Peripheral Interface SPI通讯采用一主多从模式,产生时钟的一侧称为主机,另一侧称为从机。只有一个主机(一般来说可以是微控制器/MCU),但是可以有一…...
Ubuntu20.04配置mysql配置主从复制
ubuntu20.04:mysql主库 sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf # 修改完毕重启 sudo service mysql stop sudo service mysql start主库mysqld.cnf配置 [mysqld] ... # bind-address>->--- 127.0.0.1 # 注释掉,允许外部连接 # mysqlx-b…...
HTTP 服务器(基于go实现)
编写一个 HTTP 服务器,接收客户端 request,并将 request 中带的 header 写入 response header 解析HTTP请求头 import ("fmt""net/http" )func headers(w http.ResponseWriter, r *http.Request) {// 将request中的信息写入到 w中…...
【整数二分】
数的范围 #include<iostream> using namespace std; const int N 100000 11;int n; int q; int a[N];int key;int main(){cin>>n>>q;for(int i0;i<n;i)cin>>a[i];while(q--){cin>>key;//先去找左(大于等于)再去找右…...
开发一款AR导览导航小程序多少钱?ar地图微信小程序 ar导航 源码
随着科技的不断发展,增强现实(AR)技术在不同领域展现出了巨大的潜力。AR导览小程序作为其中的一种应用形式,为用户提供了全新的观赏和学习体验。然而,开发一款高质量的AR导览小程序需要投入大量的时间、人力和技术资源…...
Shell 编程快速入门 之 函数基础知识
目录 shell函数基础知识 函数定义 函数名 函数体 参数 返回值 return返回值的含义 return与echo返回值的区别 可变参数函数 自定义库函数 定义库函数 调用库函数 执行结果 递归函数 阶乘函数 斐波那契函数 shell函数基础知识 函数定义 函数名 Shell函数用…...
Flink流批一体计算(18):PyFlink DataStream API之计算和Sink
目录 1. 在上节数据流上执行转换操作,或者使用 sink 将数据写入外部系统。 2. File Sink File Sink Format Types Row-encoded Formats Bulk-encoded Formats 桶分配 滚动策略 3. 如何输出结果 Print 集合数据到客户端,execute_and_collect…...
03.sqlite3学习——数据类型
目录 sqlite3学习——数据类型 SQL语句的功能 SQL语法 SQL命令 SQL数据类型 数字类型 整型 浮点型 定点型decimal 浮点型 VS decimal 日期类型 字符串类型 CHAR和VARCHAR BLOB和TEXT SQLite 数据类型 SQLite 存储类 SQLite 亲和类型(Affinity)及类型名称 Boo…...
LLM-chatgpt训练过程
流程简介 主要包含模型预训练和指令微调两个阶段 模型预训练:搜集海量的文本数据,无监督的训练自回归decoder; O T P ( O t < T ) O_TP(O_{t<T}) OTP(Ot<T),损失函数CE loss指令微调:在输入文本中加入…...
【学习笔记】[ABC274Ex] XOR Sum of Arrays
有点难😅 真的是 A B C ABC ABC的难度吗😅 非常精妙的哈希题目。 定义矩阵乘法: c i , j ⊕ ( a i , k & b k , j ) c_{i,j}\oplus (a_{i,k}\& b_{k,j}) ci,j⊕(ai,k&bk,j) 之所以可以矩阵乘法是因为满足 ( a ⊕ b )…...
抖音web频道爬虫
抖音web频道爬虫代码: <?php header(Content-Type:application/json; charsetutf-8);//抖音频道爬虫class DouyinChannel{private $app_id 1;private $spider_code 1;private $channels [["channel_name" > "热点","url"…...
sql中的替换函数replace()总结
1,表达式 --replace()--语法: REPLACE ( string_expression , string_pattern , string_replacement )--参数:string_expression:字符串表达式string_pattern:想要查找的子字符串string_replacement&#…...
vue3 vite使用 monaco-editor 报错
报错:Unexpected usage at EditorSimpleWorker.loadForeignModule 修改配置: "monaco-editor-webpack-plugin": "^4.2.0",删除不用 版本: "monaco-editor": "^0.28.1", 修改如下: opti…...
微信小程序获取蓝牙权限
要获取微信小程序中的蓝牙权限,您可以按照以下步骤进行操作: 1. 在 app.json 文件中添加以下代码: "permissions": { "scope.userLocation": { "desc": "需要获取您的地理位置授权以搜索…...
网络网站建设推广/青岛seo推广公司
几种shell的简介 Bourne Shell (/bin/sh) 伯努利Shell,最原始的shellBourne Again Shell 升级版的Bourne Shell,完全兼容Bourne Shell 但是在其基础上进行了很大的扩展。一般大部分Linux默认的Shell都是此Shell. Shell脚本的运行方式 修改文件的权限位,使脚本文件…...
企业电子商务网站建设问题/域名注册官网免费
(1)内存对齐 (2)内存对齐原则 结构体变量的起始地址能够被其最宽的成员大小整除结构体每个成员相对于起始地址的偏移能够被其自身大小整除,如果不能则在前一个成员后面补充字节结构体总体大小能够被最宽的成员的大小整除,如不能则在后面补充字节 (3) …...
万网的网站代码怎么看/怎么注册自己的网站域名
Objective C内存管理之理解autorelease Autorelease实际上只是把对release的调用延迟了,对于每一个Autorelease,系统只是把该Object放入了当前的Autorelease pool中,当该pool被释放时,该pool中的所有Object会被调用Release。 &…...
做网站编辑是不是也要做推广/seo搜索引擎优化论文
最近因为数学建模3天速成Python,然后做了一道网络的题,要画网络图。在网上找了一些,发现都是一些很基础的丑陋红点图,并且关于网络的一些算法也没有讲,于是自己进http://networkx.github.io/学习了一下。以下仅博主自己的总结&…...
平面设计师需要学历/seo服务是什么
2019独角兽企业重金招聘Python工程师标准>>> 环境准备:以下所有部署操作都在vm虚拟机环境下进行实现,如果转为生产环境只需配置对应ip地址即可,linux下基础配置请参考文摘CentOs6.5基本环境配置(一):基本环境配置 http…...
免费的百度网站怎么做/软文推广收费
从设计模块化企业应用程序的每个人,我有兴趣知道你如何看待模块化以及你究竟是什么参数?>基于层的模块化(如控制器/ Web模块,服务模块,域模块)是更好的方法吗?>或基于特征的模块化更好?为什么?>在基于特征的模块化的情况…...