整數溢位(Overflow)與下溢(Underflow)是智能合約中的一種安全漏洞,源於以太坊虛擬機(EVM)中整數資料型別的固定大小限制。EVM 使用固定長度的整數型別,例如:
- uint8(無符號 8 位整數):只能表示 0 到 255 的數字。
- int8(有符號 8 位整數):可表示 -128 到 127 的數字。
當算術運算結果超出型別的範圍時,會發生溢位或下溢:
- 溢位(Overflow):試圖將值設為超出型別最大範圍。例如,將
uint8
的值 255 加 1,結果不是 256,而是「回繞」到 0。 - 下溢(Underflow):試圖將值減到小於型別最小範圍。例如,將
uint8
的值 0 減 1,結果不是 -1,而是「回繞」到 255。 - 有符號整數:對於
int8
,如果從 -128 減 1,結果會回繞到 127。
這種行為類似於汽車里程表(達到 999999 後重置為 000000)或週期性數學函數(如正弦函數)。
重要注意事項:
- 在 Solidity 0.8.0 及以上版本,編譯器內建了溢位和下溢檢查,當發生溢位或下溢時,交易會自動回滾。
- Solidity 0.8.0 引入了
unchecked
關鍵字,允許開發者在特定情況下禁用這些檢查以節省 gas(例如,當確定不會發生溢位時)。這會讓算術運行為回歸到舊版本的行為。
範例(存在漏洞的合約)
以下是一個易受整數溢位和下溢攻擊的合約範例:
// SPDX-License-Identifier: MIT
pragma solidity ^0.4.17;
contract Solidity_OverflowUnderflow {
uint8 public balance;
constructor() public {
balance = 255; // Maximum value of uint8
}
// Increments the balance by a given value
function increment(uint8 value) public {
balance += value; // Vulnerable to overflow
}
// Decrements the balance by a given value
function decrement(uint8 value) public {
balance -= value; // Vulnerable to underflow
}
}
漏洞分析:
- 溢位漏洞:假設
balance = 255
,調用increment(1)
會導致255 + 1 = 0
(溢位),因為uint8
最大值為 255,超過後回繞到 0。 - 下溢漏洞:假設
balance = 0
,調用decrement(1)
會導致0 - 1 = 255
(下溢)。 - 攻擊情境:
- 攻擊者可調用
increment(1)
將balance
從 255 變為 0,造成餘額錯誤重置。 - 攻擊者可調用
decrement(1)
從 0 變為 255,製造大量虛假餘額,進而提領不屬於自己的資金。
影響
整數溢位和下溢可能導致以下嚴重後果:
- 資金損失:攻擊者可通過溢位或下溢操縱餘額,提取超出其合法擁有的資金。
- 邏輯破壞:錯誤的數值可能改變合約的預期行為,例如允許未授權的操作或生成過量代幣。
- 協議破壞:這些漏洞可能導致協議功能失常,影響用戶信任和生態系統穩定。
修復方法
為防止整數溢位和下溢,開發者應採取以下措施:
使用 Solidity 0.8.0 或更高版本:
- 從 Solidity 0.8.0 開始,編譯器自動檢查溢位和下溢,交易會在發生異常時回滾。
- 這是最簡單的修復方法,無需額外程式碼。
使用安全數學庫:
- 對於低於 0.8.0 的版本,可使用 OpenZeppelin 的 SafeMath 庫,該庫提供安全的算術運算函數(如
add()
、sub()
),在溢位或下溢時自動回滾。
手動檢查:
- 在關鍵運算前手動檢查輸入值,確保結果不會超出範圍。
修復後的範例合約
以下是修復後的合約,使用 Solidity 0.8.0 的內建溢位檢查,並加入額外防護:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract Solidity_OverflowUnderflow {
uint8 public balance;
constructor() {
balance = 255; // Maximum value of uint8
}
// Increments the balance by a given value
function increment(uint8 value) public {
balance += value; // Solidity 0.8.x automatically checks for overflow
}
// Decrements the balance by a given value
function decrement(uint8 value) public {
require(balance >= value, "Underflow detected");
balance -= value;
}
}
修復內容:
Solidity 0.8.0 溢位檢查:
- 在
increment
中,balance += value
若導致溢位(例如,255 + 1),編譯器會自動回滾交易。
手動下溢檢查:
- 在
decrement
中,新增require(balance >= value)
,確保不會發生下溢。
總結
整數溢位和下溢是智能合約中的危險漏洞,可能導致餘額操縱、未授權操作和資金損失。這些問題源於 EVM 整數型別的固定範圍限制,導致數值回繞行為。Solidity 0.8.0 及以上版本的內建溢位檢查是最簡單的解決方案,結合 OpenZeppelin 的 SafeMath 庫或手動檢查可進一步增強安全性。修復後的合約展示了如何利用現代 Solidity 功能和額外檢查來防止這些漏洞,確保合約的可靠性和安全性。開發者在設計合約時應始終使用最新版本並進行全面測試。