区块链安全—整数溢出原理分析
字数 1046 2025-08-22 12:22:15
区块链安全:Solidity整数溢出漏洞原理与防御
一、整数溢出基础概念
1. Solidity中的整数类型
Solidity提供了多种整数类型:
- 无符号整数(uint):只能表示非负数(0及正数)
- 有符号整数(int):可以表示负数,但正数范围缩小
- 支持从8位到256位,以8递增(uint8到uint256,int8到int256)
- 默认情况下,uint和int分别表示uint256和int256
2. 数值范围示例
- uint8:0 ~ 255 (二进制:0b00000000 ~ 0b11111111)
- int8:-127 ~ 127 (二进制:0b11111111 ~ 0b01111111,最高位表示符号)
二、整数溢出原理
1. 上溢(Overflow)
当数值超过类型能表示的最大值时发生上溢:
function test() returns(uint8) {
uint8 a = 255;
uint8 b = 1;
return a + b; // 返回0而不是256
}
2. 下溢(Underflow)
当数值低于类型能表示的最小值时发生下溢:
function test_1() returns(uint8) {
uint8 a = 0;
uint8 b = 1;
return a - b; // 返回255而不是-1
}
三、真实案例分析
1. 时间锁合约漏洞
contract TimeLock {
mapping(address => uint256) public lockTime;
function increaseLockTime(uint _secondsToIncrease) public {
lockTime[msg.sender] += _secondsToIncrease;
}
}
攻击方式:
- 用户存入资金,锁定1周
- 攻击者计算
2^256 - userLockTime作为_secondsToIncrease - 加法操作导致溢出,
lockTime变为0 - 立即提取资金
2. SMT合约转账漏洞
function transferProxy(address _from, address _to, uint256 _value, uint256 _feeSmt) public {
if(balances[_from] < _feeSmt + _value) revert();
// ...
}
攻击方式:
- 设置
_feeSmt和_value使它们的和溢出为很小的值 - 例如:
_feeSmt = 0x8ffff...ffff,_value = 0x7000...0001 - 两者相加结果为0,绕过余额检查
3. BEC批量转账漏洞
function batchTransfer(address[] _receivers, uint256 _value) public {
uint cnt = _receivers.length;
uint256 amount = uint256(cnt) * _value;
require(balances[msg.sender] >= amount);
// ...
}
攻击方式:
- 控制
_receivers.length和_value使乘积溢出 - 例如:设置
cnt和_value使amount变为极小的值 - 绕过余额检查,实现大量转账
四、防御措施
1. 使用SafeMath库
import "./SafeMath.sol";
contract SafeContract {
using SafeMath for uint256;
function safeAdd(uint256 a, uint256 b) public pure returns (uint256) {
return a.add(b); // 自动检查溢出
}
}
2. 手动检查溢出
// 加法检查
if(a + b < a) revert();
// 乘法检查
if(a * b / a != b) revert();
// 减法检查
if(b > a) revert();
3. 代码修复示例
function transferProxy(address _from, address _to, uint256 _value, uint256 _feeSmt) public {
// 检查_feeSmt + _value是否溢出
if(_feeSmt + _value < _value) revert();
// 检查余额是否足够
if(balances[_from] < _feeSmt + _value) revert();
// 检查接收方余额是否溢出
if(balances[_to] + _value < balances[_to]) revert();
// 正常处理逻辑...
}
五、实战演练
Ethernaut挑战题
contract Token {
mapping(address => uint) balances;
function transfer(address _to, uint _value) public returns (bool) {
require(balances[msg.sender] - _value >= 0);
balances[msg.sender] -= _value;
balances[_to] += _value;
return true;
}
}
攻击步骤:
- 初始余额为20
- 调用transfer传入_value=21
balances[msg.sender] - _value下溢变为极大值- 绕过检查,实现非法转账
六、总结
整数溢出漏洞虽然原理简单,但在智能合约中危害严重。防御措施包括:
- 始终使用SafeMath库进行算术运算
- 对用户输入进行严格验证
- 在关键操作前添加溢出检查
- 进行充分的测试,包括边界条件测试
通过理解这些原理和案例,开发者可以更好地编写安全的智能合约代码。