Solidity 合约安全,常见漏洞 (下篇)
Solidity 合约安全,常见漏洞 (下篇)
Solidity 合约安全,常见漏洞 (上篇)
不安全的随机数
目前不可能用区块链上的单一交易安全地产生随机数。区块链需要是完全确定的,否则分布式节点将无法达成关于状态的共识。因为它们是完全确定的,所以任何 "随机"的数字都可以被预测到。下面的掷骰子函数可以被利用。
contract UnsafeDice {function randomness() internal returns (uint256) {return keccak256(abi.encode(msg.sender, tx.origin, block.timestamp, tx.gasprice, blockhash(block.number - 1);}// our dice can land on one of {0,1,2,3,4,5}function rollDice() public payable {require(msg.value == 1 ether);if (randomness() % 6) == 5) {msg.sender.call{value: 2 ether}("");}}
}contract ExploitDice {function randomness() internal returns (uint256) {return keccak256(abi.encode(msg.sender, tx.origin, block.timestamp, tx.gasprice, blockhash(block.number - 1);}function betSafely(IUnsafeDice game) public payable {if (randomness % 6) == 5)) {game.betSafely{value: 1 ether}()}// else don't do anything}
}
如何来产生随机数并不重要,因为攻击者可以完全复制它。使用更多的 "熵"的来源,如 msg.sender、时间戳等,不会有任何影响,因为智能合约也可以预测它。
错误使用 Chainlink 随机数 Oracle
Chainlink 是一个流行的解决方案,以获得安全的随机数。它分两步进行。首先,智能合约向预言机处发送一个随机数请求,然后在一些区块之后,预言机以一个随机数作为回应。
由于攻击者无法预测未来,所以他们无法预测随机数。
除非智能合约错误地使用预言机:
- 请求随机数的智能合约必须在随机数返回之前不做任何事情。否则,攻击者可以监视返回随机数的预言机的 mempool,并在前面运行预言机,知道随机数会是什么。
- 随机数预言机本身可能会试图操纵你的应用程序。如果没有其他节点的共识,他们不能挑选随机数,但如果你的应用程序同时请求几个随机数,他们可以扣留和重新排序。
- 最终性在以太坊或大多数其他 EVM 链上不是即时的。仅仅因为某些区块是最新的,并不意味着它不一定会保持这种状态。这被称为 “链上重组”。事实上,链可以改变的不仅仅是最后一个区块。这就是所谓的 “深度重组”。Etherscan 报告了各种链的 re-orgs,例如以太坊重组和 Polygon 重组。在 Polygon 上,重组的深度可以达到 30 个或更多的区块,所以等待更少的区块会使应用变得脆弱(当 zk-evm 成为 Polygon 上的标准共识时,这种情况可能会改变,因为最终性将与以太坊的一致,但这是未来的预测,而不是目前的事实)。
- 下面是其他 Chainlink 随机数的安全考虑。
从价格 Oracle 中获取陈旧的数据
Chainlink 没有 SLA(服务水平协议)来保持它的价格预言机在一定时间范围内的更新。当链上的交易严重拥堵时,价格更新可能会被延迟。
使用价格预言机的智能合约必须明确地检查数据是否陈旧,即最近在某个阈值内被更新。否则,它不能对价格做出可靠的决策。
还有一个更复杂的问题,如果价格没有变化超过偏差阈值,预言机可能不会更新价格以节省 Gas,所以这可能会影响到什么时间阈值被认为是 “陈旧”。
了解智能合约所依赖的 Oracle 的服务水平协议是很重要的。
只依赖一个预言机
无论一个预言机看起来多么安全,将来都可能发现攻击。对此的唯一防御措施是使用多个独立的预言机。
一般来说,预言机是很难正确的
区块链可以是相当安全的,但首先把数据放到链上就必须进行某种链外操作,这就放弃了区块链提供的所有安全保证。即使预言机者保持诚实,他们的数据来源也可以被操纵。例如,一个信使可以可靠地报告来自中心化交易所的价格,但这些价格可以被大量的买入和卖出订单所操纵。同样,依赖于传感器数据或一些 web2 API 的预言机也会受到传统黑客攻击的影响。
一个好的智能合约架构在可能的情况下会完全避免使用预言机。
混合计算
考虑以下合约
contract MixedAccounting {uint256 myBalance;function deposit() public payable {myBalance = myBalance + msg.value;}function myBalanceIntrospect() public view returns (uint256) {return address(this).balance;}function myBalanceVariable() public view returns (uint256) {return myBalance;}function notAlwaysTrue() public view returns (bool) {return myBalanceIntrospect() == myBalanceVariable();}
}
上面的合约没有接收或回退函数,所以直接将以太传送给它就会回退。然而,合约可以用自毁的方式强行向它发送以太。在此案例中,myBalanceIntrospect()会比 myBalanceVariable() 大。两种以太币的计算方法都没有问题,但如果你同时使用这两种方法,那么合约可能会有不一致的行为。
这同样适用于 ERC20 代币。
contract MixedAccountingERC20 {IERC20 token;uint256 myTokenBalance;function deposit(uint256 amount) public {token.transferFrom(msg.sender, address(this), amount);myTokenBalance = myTokenBalance + amount;}function myBalanceIntrospect() public view returns (uint256) {return token.balanceOf(address(this));}function myBalanceVariable() public view returns (uint256) {return myTokenBalance;}function notAlwaysTrue() public view returns (bool) {return myBalanceIntrospect() == myBalanceVariable();}
}
我们再次不能假设 myBalanceIntrospect()和 myBalanceVariable()总是返回相同的值。可以直接将 ERC20 代币转账到 MixedAccountingERC20,绕过存款函数,不更新 myTokenBalance 变量。
在用反省检查余额时,应避免严格使用相等检查,因为余额可以被外人随意改变。
把加密证明当作密码一样对待
这不是 Solidity 的一个怪癖,更多的是开发者对如何使用密码学来赋予地址特殊权限有普遍误解。下面的代码是不安全的:
contract InsecureMerkleRoot {bytes32 merkleRoot;function airdrop(bytes[] calldata proof, bytes32 leaf) external {require(MerkleProof.verifyCalldata(proof, merkleRoot, leaf), "not verified");require(!alreadyClaimed[leaf], "already claimed airdrop");alreadyClaimed[leaf] = true;mint(msg.sender, AIRDROP_AMOUNT);}
}
这段代码是不安全的,原因有三:
- 任何知道被选中进行空投的地址的人都可以重新创建 Merkle 树并创造一个有效的证明。
- 叶子没有 Hash。攻击者可以提交一个与 Merkle 根相同的叶子,并绕过 require 语句。
- 即使上述两个问题被修复,一旦有人提交了有效的证明,他们就可以被抢跑。
加密证明(Merkle 树、签名等)需要与 msg.sender 绑定,攻击者在没有获得私钥的情况下无法操纵。
Solidity 不会向上转型 uint 大小
function limitedMultiply(uint8 a, uint8 b) public pure returns (uint256 product) {product = a * b;
}
尽管 product 是一个uint256变量,但乘法结果不会大于 255,否则代码将被回退。
这个问题可以通过向上转型每个变量来解决:
function unlimitedMultiply(uint8 a, uint8 b) public pure returns (uint256 product) {product = uint256(a) * uint256(b);
}
在结构中的整数相乘,也会出现这样的情况。当乘以在结构中的小数值时,你应该注意到这一点:
struct Packed {uint8 time;uint16 rewardRate
}//...Packed p;
p.time * p.rewardRate; // this might revert!
Solidity 截断不会回退
Solidity 并不检查将一个整数转换为一个较小的整数是否安全。除非某些业务逻辑能确保向下转型是安全的,否则应该使用 SafeCast 这样的库。
function test(int256 value) public pure returns (int8) {return int8(value + 1); // overflows and does not revert
}
对存储指针的写入不会保存新数据
这段代码看起来像是把 myArray[1]中的数据复制到了 myArray[0]中,但其实不是。如果你把函数的最后一行注释掉,编译器会说这个函数应该变成一个视图函数。对 foo 的写入并没有写到底层存储。
contract DoesNotWrite {struct Foo {uint256 bar;}Foo[] public myArray;function moveToSlot0() external {Foo storage foo = myArray[0];foo = myArray[1]; // myArray[0] 不会改变// we do this to make the function a state// changing operation// and silence the compiler warningmyArray[1] = Foo({bar: 100});}
}
所以不要写到存储指针。
删除包含动态数据类型的结构体并不会删除动态数据
如果一个映射(或动态数组)在一个结构体内,并且该结构被删除,那么映射或数组将不会被删除。
除了删除数组之外,删除关键字只能删除一个存储槽。如果该存储槽包含对其他存储槽的引用,这些存储槽不会被删除。
contract NestedDelete {mapping(uint256 => Foo) buzz;struct Foo {mapping(uint256 => uint256) bar;}Foo foo;function addToFoo(uint256 i) external {buzz[i].bar[5] = 6;}function getFromFoo(uint256 i) external view returns (uint256) {return buzz[i].bar[5];}function deleteFoo(uint256 i) external {// internal map still holds the data in the// mapping and arraydelete buzz[i];}
}
现在让我们做以下交易序列
- addToFoo(1)
- getFromFoo(1) 返回 6
- deleteFoo(1)
- getFromFoo(1) 仍然返回 6!
记住,在 Solidity 中,map 永远不会是 "空"的。因此,如果有人访问一个已经被删除的项目,交易将不会回退,而是返回该数据类型的零值。
相关文章:
Solidity 合约安全,常见漏洞 (下篇)
Solidity 合约安全,常见漏洞 (下篇) Solidity 合约安全,常见漏洞 (上篇) 不安全的随机数 目前不可能用区块链上的单一交易安全地产生随机数。区块链需要是完全确定的,否则分布式节点将无法达…...
nodejs根据pdf模板填入中文数据并生成新的pdf文件
导入pdf-lib库和fontkit npm install pdf-lib fs npm install pdf-lib/fontkit 具体代码 const { PDFDocument, StandardFonts } require(pdf-lib); const fs require(fs); const fontkit require(pdf-lib/fontkit) let pdfDoc let font async function fillPdfForm(temp…...

UE4与pycharm联合仿真的调试问题及一些仿真经验
文章目录 ue4与pycharm联合仿真的调试问题前言ue4端的debug过程pycharm端 一些仿真经验小结 ue4与pycharm联合仿真的调试问题 前言 因为在实验中我需要用到py代码输出控制信息给到ue4中,并且希望看到py端和ue端分别在运行过程中的输出以及debug调试。所以…...

【数据分析】波士顿矩阵
波士顿矩阵是一种用于分析市场定位和企业发展战略的管理工具。由美国波士顿咨询集团(Boston Consulting Group)于1970年提出,并以该集团命名。 波士顿矩阵主要基于产品生命周期和市场份额两个维度,将企业的产品或业务分为四个象限…...

sizeof和strlen的对比
文章目录 🚩前言🚩sizeof🚩strlen🚩sizeof和strlen对比 🚩前言 很多小白在学习中,经常将sizeof和strlen弄混了。本篇文章,小编讲解一下sizeof和strlen的区别。🤷♂️ 🚩…...
Flutter系列文章-Flutter 插件开发
在本篇文章中,我们将学习如何开发 Flutter 插件,实现 Flutter 与原生平台的交互。我们将详细介绍插件的开发过程,包括如何创建插件项目、实现方法通信、处理异步任务等。最后,我们还将演示如何将插件打包并发布到 Flutter 社区。 …...

基于SpringBoot实现MySQL与Redis的数据最终一致性
问题场景 在并发场景下,MySQL和Redis之间的数据不一致性可能成为一个突出问题。这种不一致性可能由网络延迟、并发写入冲突以及异常情况处理等因素引起,导致MySQL和Redis中的数据在某些时间点不同步或出现不一致的情况。数据一致性问题的级别可以分为三…...
mysql与oracle数据库备份
mysql 1在执行mysql数据备份前,可先执行命令查看磁盘容量: # df -h Filesystem Size Used Avail Use% Mounted on /dev/mapper/VolGroup-lv_root 50G 46G 1.6G 97% / tmpfs 1.9G 92K 1.9G 1% /dev/shm /dev/sda1 485M 39M 421M 9% /boot…...

UE4 材质学习笔记
CheapContrast与CheapContrast_RGB都是提升对比度的,一个是一维输入,一个是三维输入,让亮的地方更亮,暗的地方更暗,不像power虽然也是提升对比度,但是使用过后的结果都是变暗或者最多不变(值为1…...

TiDB 源码编译之 TiProxy 篇
作者: ShawnYan 原文来源: https://tidb.net/blog/3d57f54d TiProxy 简介 TiProxy 是一个基于 Apache 2.0 协议开源的、轻量级的 TiDB 数据库代理,基于 Go 语言编写,支持 MySQL 协议。 TiProxy 支持负载均衡,接收来…...
利用驱动漏洞
sbyt3/IObitUnlocker.Wrapper (github.com)...

开始MySQL之路——MySQL约束概述详解
MySQL约束 create table [if not exists] 表名(字段名1 类型[(宽度)] [约束条件] [comment 字段说明],字段名2 类型[(宽度)] [约束条件] [comment 字段说明],字段名3 类型[(宽度)] [约束条件] [comment 字段说明] )[表的一些设置]; 概念 约束英文:constraint 约束实…...
CMake基础和命令介绍
CMake是一个跨平台的构建工具,它可以生成各种不同平台上的构建文件,例如Makefile或Visual Studio项目文件。以下是一些常用的CMake命令: 1. cmake_minimum_required:指定需要的最小CMake版本。 2. project:定义项目名…...

【matlab利用shp文件制作mask白化文件】
matlab白化文件 mask文件的作用matlab制作mask文件mask结果 mask文件的作用 地理信息绘图中的 “mask” 通常指的是遮罩或掩膜,用于在地图或图像上隐藏、高亮或标记特定区域。 数据可视化: 地理信息绘图 mask 可以用于突出显示特定地理区域,使…...

【LLM】解析pdf文档生成摘要
文章目录 一、整体思路二、代码三、小结Reference 一、整体思路 非常简单的一个v1版本 利用langchain和pdfminer切分pdf文档为k块,设置overlap等参数先利用prompt1对每个chunk文本块进行摘要生成,然后利用prompt2对多个摘要进行连贯组合/增删模型可以使…...

方案:AI边缘计算智慧工地解决方案
一、方案背景 在工程项目管理中,工程施工现场涉及面广,多种元素交叉,状况较为复杂,如人员出入、机械运行、物料运输等。特别是传统的现场管理模式依赖于管理人员的现场巡查。当发现安全风险时,需要提前报告࿰…...
【Python】【数据结构和算法】查找最大或最小的N个元素
除了直接排序,还可以利用heaq模块的nlargest()和nsmallest()方法,例如: >>> nums [3, 5, 2, 4, 1] >>> smallest heapq.nsmallest(3, nums) >>> print(smallest) [1, 2, 3] >>> largest heapq.nlarg…...

C++day1(笔记整理)
一、Xmind整理: 二、上课笔记整理: 1.第一个c程序:hello world #include <iostream> //#:预处理标识符 //<iostream>:输入输出流类所在的头文件 //istream:输入流类 //ostream:输出流类using namespace std; //std&#x…...

关于chromedriver.exe一系列问题的解决办法
最新 chromedriver.exe下载地址:https://googlechromelabs.github.io/chrome-for-testing/#stable 下载最新版本的 chromedriver.exe 将其解压在 python.exe 同目录下,以及Chrome 的路径下 例如: C:\Program Files\Google\Chrome\Applicati…...

css-选择器、常见样式、标签分类
CSS CSS简介 层叠样式表(英文全称:Cascading Style Sheets)是一种用来表现HTML(标准通用标记语言的一个应用)或XML(标准通用标记语言的一个子集)等文件样式的计算机语言。CSS不仅可以静态地修饰网页,还可…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...

【JavaWeb】Docker项目部署
引言 之前学习了Linux操作系统的常见命令,在Linux上安装软件,以及如何在Linux上部署一个单体项目,大多数同学都会有相同的感受,那就是麻烦。 核心体现在三点: 命令太多了,记不住 软件安装包名字复杂&…...

python执行测试用例,allure报乱码且未成功生成报告
allure执行测试用例时显示乱码:‘allure’ �����ڲ����ⲿ���Ҳ���ǿ�&am…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官
。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量:setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...
go 里面的指针
指针 在 Go 中,指针(pointer)是一个变量的内存地址,就像 C 语言那样: a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10,通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...

解析“道作为序位生成器”的核心原理
解析“道作为序位生成器”的核心原理 以下完整展开道函数的零点调控机制,重点解析"道作为序位生成器"的核心原理与实现框架: 一、道函数的零点调控机制 1. 道作为序位生成器 道在认知坐标系$(x_{\text{物}}, y_{\text{意}}, z_{\text{文}}…...

链式法则中 复合函数的推导路径 多变量“信息传递路径”
非常好,我们将之前关于偏导数链式法则中不能“约掉”偏导符号的问题,统一使用 二重复合函数: z f ( u ( x , y ) , v ( x , y ) ) \boxed{z f(u(x,y),\ v(x,y))} zf(u(x,y), v(x,y)) 来全面说明。我们会展示其全微分形式(偏导…...

【PX4飞控】mavros gps相关话题分析,经纬度海拔获取方法,卫星数锁定状态获取方法
使用 ROS1-Noetic 和 mavros v1.20.1, 携带经纬度海拔的话题主要有三个: /mavros/global_position/raw/fix/mavros/gpsstatus/gps1/raw/mavros/global_position/global 查看 mavros 源码,来分析他们的发布过程。发现前两个话题都对应了同一…...