智慧合約的安全
阿新 • • 發佈:2018-12-13
智慧合約的安全問題一直是編寫智慧合約的關鍵點。多數的智慧合約都是開源的,原始碼公佈更容易被黑客找到攻擊的漏洞。
這裡將一些常見的,易犯的錯誤。首先我們先看看下面這段程式碼:
contract text{ address owner; function userWallet() public{ owner == msg.sender; } function transferto(address add,uint num) public payable{ if(tx.origin == owner){ add.transfer(num); } } }
這裡先講講其中tx.origin和msg.sender不同。msg.sender指的是呼叫合約的地址,而tx.origin指的是發起transaction的地址。舉個例子,看下面的程式碼。
pragma solidity^0.4.7; contract c1{ address add1; address add2; function findAdd() public { add1 = msg.sender; add2 = tx.origin; } function getAdd1() public returns(address){ return add1; } function getAdd2() public returns(address){ return add2; } } contract differ{ address public a1; address public a2; function f1() public { c1 c = new c1(); c.findAdd(); a1 = c.getAdd1(); a2 = c.getAdd2(); } }
執行完合約後,a1就是合約differ的地址,而a2是呼叫合約diiffer的地址,也就是發起transaction的地址。上面簡單講了tx.origin和msg.sender的區別。接下來我們回到第一個合約中,這個合約實現了一個轉賬的功能·。但這個合約存在bug。黑客可以利用這個漏洞進行攻擊。比如下面這段程式碼
contract attack{ address hack; constructor() public{ hack = msg.sender; } function () external{ text(msg.sender).transferto(hack,msg.sender.balance); } }
只要讓第一個text合約就會觸發attack合約中的匿名函式,這時就會向hack地址轉賬了。因此在text合約中應該使用msg.sender而不是tx.origin。
接下來還有一個不容易被找出來的錯誤,比如下面的合約
pragma solidity^0.4.7; contract fund{ mapping(address=>uint) num; function transferto(address add)public payable{ if(num[add] != 0){ add.transfer(num[add]); num[add] = 0; } } }
這個合約實現一個兌換的功能,可以將每個address中所佔的數兌換成以太幣,黑客可以實現這樣一個合約
contract attack{ address hack; constructor()public{ hack = msg.sender; } function () external { fund(msg.sender).transferto(hack); } }
這樣合約fund每次轉賬都會呼叫attack合約的匿名函式,而匿名函式中又會呼叫合約fund中的轉賬。便會一直重複,這時候為了防止這種情況。可以改成下面的程式碼
contract fund{ mapping(address=>uint) num; function transferto(address add)public payable{ uint temp = num[add]; if(temp != 0){ num[add] = 0; add.transfer(temp); } } }
將兌換的num在轉賬之前重置為0。這樣即使用上述的程式碼進行攻擊亦不會再次執行轉賬了。