1. 程式人生 > >solidity智慧合約[49]-安全-溢位***

solidity智慧合約[49]-安全-溢位***

***回顧

1
2
3
4
5
6
7
2016年6月,以太坊最大眾籌專案The DAO被***,***獲得超過350萬個以太幣,最終導致以太坊分叉為ETH和ETC。
2016年拒絕服務***:GovernMental's 1100 ETH
2016年KotET(“紛爭時代”)合約遭受***。
2017年Parity錢包,遭受delecate call注入,銷燬了合約。損失513,774.16 Ether
2017年ANT Token遭受重入漏洞。
2017年Simoleon合約被***。***通過部署***合約獲得了超過700萬的token,從57萬賬戶中脫穎而出,一舉成為該合約token的第四大持有者。

2018年BEC代幣遭到襲擊,***手法被披露的24小時內,就有30多個合約遭受***

溢位

孔子曾經說過 過猶不及。做事情都有限度,一旦超過了限度就會適得其反。理解溢位問題最好的是在千禧之年爆發的千年蟲事件。過去,由於計算機程式中使用兩個數字來表示年份,如1998年被表示為“98”、1999年被表示為“99”;而2000年被表示為“00”,這樣將會導致某些程式在計算時得到不正確的結果,如把“00”誤解為1900年。在嵌入式系統中可能存在同樣的問題,這有可能導致裝置停止運轉或者發生更加災難性的後果。

solidity中的溢位問題

下面是一個簡單的函式,其功能是將桉樹加1.例如傳遞4,返回5。傳遞200,返回201。但是裡面暗藏著陷阱。例如當傳遞255的時候,會返回0…這就是溢位。這是由於uint8的最大值為255,在記憶體中:為1111 1111

。一旦加1之後,變為了
1 0000 0000,但是最大的位數為8位。截斷之後,變為了0000 0000 因此返回的結果為0。

1
2
3
function add(uint8 a) public pure returns(uint8){
   return a+1;
}

safeMath避免溢位問題

因此我們可以看到,對於4則運算,很容易的發生溢位問題。OpenZeppelin 建立了一個叫做 SafeMath 的 庫。這就規避掉溢位問題。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
library SafeMath {

   function add(uint a, uint b) internal pure returns (uint c) {
       c = a + b;
       require(c >= a);
   }
   function sub(uint a, uint b) internal pure returns (uint c) {
       require(b <= a);
       c = a - b;
   }
   function mul(uint a, uint b) internal pure returns (uint c) {
       c = a * b;
       require(a == 0 || c / a == b);
   }
   function div(uint a, uint b) internal pure returns (uint c) {
       require(b > 0);
       c = a / b;
   }
}

BEC代幣***全紀實

如下為BEC代幣的原始碼抽離出來的部分。2018年4月份BEC代幣遭到***的溢位襲擊。***為自己的兩個賬號轉移了2^255次方的代幣。導致市場的恐慌,幣價一度一文不值。***手法被披露的24小時內,就有類似30多個合約遭受***

在TokenExample合約中,有一個batchTransfer函式。此函式的功能為對賬戶進行轉賬操作。第一個引數為動態長度地址,明確要轉賬的賬戶。第二個引數為轉賬的金額。 要轉賬成功,必須要保證在balance資金錶中,傳送者必須有超過總金額(賬戶數量轉賬金額)。但是 uint256 amount = uint256(cnt) value;這段程式碼並沒有做安全的乘法,導致可能會發生溢位***。當***呼叫合約的時候,在remix中:
輸入地址陣列以及:[“0xb4D30Cac5124b46C2Df0CF3e3e1Be05f42119033”,“0x0e823fFE018727585EaF5Bc769Fa80472F76C3d7”],
以及
value"0x8000000000000000000000000000000000000000000000000000000000000000"即2*255,
使得amount=2^255 * 2,超出uint256型別的範圍[0,2**256-1],溢位為0,傳送者賬戶餘額不減少,並且,本例中,傳送者的代幣可以為零,實現"無中生有"。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39

library SafeMath{
   function sub(uint256 a,uint256 b) internal pure returns(uint256){
       assert(b<=a);
       return a-b;
   }

   function add(uint256 a,uint256 b) internal pure returns(uint256 c){
       c=a+b;
       assert(c>=a);
       return c;
   }
}

contract TokenExample{
   //使用safemath庫
   using SafeMath for uint256;

   //資金錶
   mapping(address=>uint256) public balance;
   function batchTransfer(address[] _receivers,uint256 _value) public returns(bool){
       //要轉移的地址的數量  
       uint cnt = _receivers.length;
       //轉賬總金額
       uint256 amount = uint256(cnt)*_value;
       //判斷轉賬地址必須大於0
       require(cnt >0 && cnt <=20);
       //判斷髮送者擁有的金額必須大於轉賬的總金額
       require(_value >0 && balance[msg.sender]>=amount);
       //傳送者賬戶金額減少 20
       balance[msg.sender] = balance[msg.sender].sub(amount);
       //接受者金額增加   10
       for(uint i =0 ;i<cnt;i++){
           balance[_receivers[i]] = balance[_receivers[i]].add(_value);
       }

       return true;
   }
}