福彩浙江快乐彩走势图:浙江快乐彩开奖查询

如果智能合約被攻擊了怎么辦

浙江快乐彩开奖查询 www.vgwrn.com 雖然區塊鏈和智能合約技術每天都在革新,但是風險依然很高。攻擊者從沒有放棄去尋找機會來攻擊這些合約。

如果你在數字貨幣世界待過足夠時間,也許你聽說過1或2個智能合約攻擊時間,這些攻擊導致了幾千萬美元的盜竊損失。最著名的攻擊是DAO事件,這是數字貨幣世界最受期待的項目之一,同時也是智能合約的改革。雖然很多人聽說過這些攻擊,但是很少人知道到底發生了什么,是怎么發生的,以及如何避免這些錯誤。Y55顯卡之家

智能合約是動態的,復雜的以及難以置信地強大。雖然他們的潛力是很難想象,但是也不可能一夜之間就成為了攻擊的對象。也就是說,對于往后的數字貨幣,我們可以從之前的錯誤中學到經驗,然后一起成長。雖然DAO是已經發生的事情,但是這對于開發者,投資者,以及社區成員對于智能合約攻擊來說,都是一個很好的例子。Y55顯卡之家

今天,我想和大家聊聊從DAO事件中,我們學到的3件事。Y55顯卡之家

攻擊#1:重入攻擊Y55顯卡之家

當攻擊者通過對目標調用提款操作的時候,重入攻擊就會發生,就好像DAO事件一樣。當合約不能在發出資金之前更新狀態(用戶余額),攻擊者就可以連續進行提取函數調用,來獲得合約中的資金。任何時候攻擊者獲得以太幣,他的合約都會自動地調用反饋函數,function (),這就再次調用了提現合約。這時候,攻擊就會進入遞歸回路,這時候這個合約中的資金就會轉入攻擊者。因為目標合約都在不停地調用攻擊者的函數,這個合約也不會更新攻擊者的余額。當前的合約不會發現有任何問題,更清楚地說,合約函數中包含反饋函數,當合約收到以太幣和零數據的時候,合約函數就會自動執行。Y55顯卡之家

如果智能合約被攻擊了怎么辦Y55顯卡之家

攻擊流程Y55顯卡之家

1.攻擊者將以太幣存入目標函數Y55顯卡之家

2.目標函數就會根據存入的以太幣而更新攻擊者的約Y55顯卡之家

3.攻擊者請求拿回資金Y55顯卡之家

4.資金就會退回Y55顯卡之家

5.攻擊者的反饋函數生效,然后調用提現功能Y55顯卡之家

6.智能合約的邏輯就會更新攻擊者的余額,因為提現又被成功調用Y55顯卡之家

7.資金發送到攻擊者Y55顯卡之家

8.第5-7步重復使用Y55顯卡之家

9.一旦攻擊結束,攻擊者就會把資金從他們自己的合約發送到個人地址Y55顯卡之家

1*UeDgMZo2n0skHzgkl352zQY55顯卡之家

重入攻擊的遞歸回路Y55顯卡之家

很不幸地是,一旦這個攻擊開始,無法停下。攻擊者的提現功能會被一次次地調用,直到合約中的燃料跑完,或者被害者的以太幣余額被消耗光。Y55顯卡之家

代碼Y55顯卡之家

下面就是DAO合約的簡單版本,其中會包括一些介紹來為這些不熟悉代碼/ solidity語言更好地理解合約。Y55顯卡之家

contract babyDAO {Y55顯卡之家

/* assign key/value pair so we can look upY55顯卡之家

credit integers with an ETH address */Y55顯卡之家

mapping (address => uint256) public credit;Y55顯卡之家

/* a function for funds to be added to the contract,Y55顯卡之家

sender will be credited amount sent */Y55顯卡之家

function donate(address to) payable {Y55顯卡之家

credit[msg.sender] += msg.value;Y55顯卡之家

Y55顯卡之家

/*show ether credited to address*/Y55顯卡之家

function assignedCredit(address) returns (uint) {Y55顯卡之家

return credit[msg.sender];Y55顯卡之家

Y55顯卡之家

/*withdrawal ether from contract*/Y55顯卡之家

function withdraw(uint amount) {Y55顯卡之家

if (credit[msg.sender] >= amount) {Y55顯卡之家

msg.sender.call.value(amount)();Y55顯卡之家

credit[msg.sender] -= amount;Y55顯卡之家

Y55顯卡之家

Y55顯卡之家

Y55顯卡之家

如果我們看下函數withdraw(),我們可以看到DAO合約使用address.call.value()來發送資金到msg.sender。不僅如此,在資金發出后,合約會更新credit[msg.sender]的狀態。攻擊者在發現了合約代碼中的問題,就能夠使用類似下面的ThisIsAHodlUp {}來將資金轉入contract babyDAO{}合約。Y55顯卡之家

import ‘browser/babyDAO.sol’;Y55顯卡之家

contract ThisIsAHodlUp {Y55顯卡之家

/* assign babyDAO contract as "dao" */Y55顯卡之家

babyDAO public dao = babyDAO(0x2ae...);Y55顯卡之家

address owner;Y55顯卡之家

/*assign contract creator as owner*/Y55顯卡之家

constructor(ThisIsAHodlUp) public {Y55顯卡之家

owner = msg.sender;Y55顯卡之家

Y55顯卡之家

/*fallback function, withdraws funds from babyDAO*/Y55顯卡之家

function() public {Y55顯卡之家

dao.withdraw(dao.assignedCredit(this));Y55顯卡之家

Y55顯卡之家

/*send drained funds to attacker’s address*/Y55顯卡之家

function drainFunds() payable public{Y55顯卡之家

owner.transfer(address(this).balance);Y55顯卡之家

Y55顯卡之家

Y55顯卡之家

需要注意地是,這個后退函數,function(),會調用DAO或者babyDAO{}的提現函數,來從合約中盜取資金。從另個方面來說,當攻擊者想要把所有偷竊來的資金賺到他們的地址,drainFunds()功能會被調用。Y55顯卡之家

解決方案Y55顯卡之家

現在,我們應該清楚重放攻擊會利用兩個特別的智能合約漏洞。第一個是當合約的狀態在資金發出之后,而不是之前進行更新。由于在發出資金前無法更新合約狀態,函數就會在中間計算的時候被打斷,合約也認為資金其實還沒有發出。第二個漏洞就當合約錯誤地使用address.call.value()來發出資金,而不是address.transfer() 或者 address.send()。這兩個都受限于2300gas,只記錄一個事件而不是多個外部調用。Y55顯卡之家

contract babyDAO{Y55顯卡之家

....Y55顯卡之家

function withdraw(uint amount) {Y55顯卡之家

if (credit[msg.sender] >= amount) {Y55顯卡之家

credit[msg.sender] -= amount; /* updates balance first */Y55顯卡之家

msg.sender.send(amount)(); /* send funds properly */Y55顯卡之家

Y55顯卡之家

Y55顯卡之家

攻擊2:下溢攻擊Y55顯卡之家

雖然DAO合約不會讓受害者掉入下溢攻擊,我們能夠通過現有的babyDAO contract{}來更好地理解這些攻擊為什么會發生。Y55顯卡之家

首先,我們需要理解什么是256單位制。一個256單位制是由256個字節組成。以太坊的虛擬機是使用256字節來完成的。因為以太坊虛擬機受限于256字節的大小,所以數字的范圍是0到4,294,967,295 (22??)。如果我們超過這個范圍,那么數字就會重置到范圍的最底部(22?? + 1 = 0)。如果我們低于這個范圍,這個數字就會重置到這個范圍的頂端(0–1= 22??)。Y55顯卡之家

當我們從零中減去大于零的數,就會發生下溢攻擊,導致一個新的22??數集。現在,如果攻擊者的余額發生了下溢,那么這部分余額就會更新,從而導致整個資金被盜。Y55顯卡之家

攻擊流程Y55顯卡之家

攻擊者通過發出1Wei到目標合約,來啟動攻擊。Y55顯卡之家

合約認證發出資金的人Y55顯卡之家

隨后調用1Wei的提現函數Y55顯卡之家

合約會從發送者的賬戶扣除的1Wei,現在賬戶余額又是零Y55顯卡之家

因為目標合約將以太幣發給攻擊者,攻擊者的退回函數被處罰,所以提現函數又被調用。Y55顯卡之家

提現1Wei的事件被記錄Y55顯卡之家

攻擊者合約的余額就會更新兩次,第一次是到零,第二次是到-1。Y55顯卡之家

攻擊者的余額回置到22??Y55顯卡之家

攻擊者通過提現目標合約的所有資金,從而完成整個攻擊Y55顯卡之家

代碼Y55顯卡之家

/*donate 1 wei, withdraw 1 wei*/Y55顯卡之家

function attack() {Y55顯卡之家

dao.donate.value(1)(this);Y55顯卡之家

dao.withdraw(1);Y55顯卡之家

Y55顯卡之家

/*fallback function, results in 0–1 = 2**256 */Y55顯卡之家

function() {Y55顯卡之家

if (performAttack) {Y55顯卡之家

performAttack = false;Y55顯卡之家

dao.withdraw(1);Y55顯卡之家

Y55顯卡之家

Y55顯卡之家

/*extract balance from smart contract*/Y55顯卡之家

function getJackpot() {Y55顯卡之家

dao.withdraw(dao.balance);Y55顯卡之家

owner.send(this.balance);Y55顯卡之家

Y55顯卡之家

Y55顯卡之家

解決方案Y55顯卡之家

為了防止受害人陷入下溢攻擊,最好的方法是看更新的狀態是否在字節范圍內。我們可以添加參數來檢查我們的代碼,作為最后一層?;?。函數withdraw()的首行代碼是為了檢查是否有足夠的資金,第二行是為了檢查超溢,第三個是檢查下溢。Y55顯卡之家

contract babysDAO{Y55顯卡之家

....Y55顯卡之家

/*withdrawal ether from contract*/Y55顯卡之家

function withdraw(uint amount) {Y55顯卡之家

if (credit[msg.sender] >= amountY55顯卡之家

&& credit[msg.sender] + amount >= credit[msg.sender]Y55顯卡之家

&& credit[msg.sender] - amount <= credit[msg.sender]) {Y55顯卡之家

credit[msg.sender] -= amount;Y55顯卡之家

msg.sender.send(amount)();Y55顯卡之家

Y55顯卡之家

Y55顯卡之家

需要注意,就像我們之前討論,我們上面的代碼是在發出資金之前更新用戶的余額。Y55顯卡之家

攻擊#3:跨函數競爭條件Y55顯卡之家

最后要說的,就是跨函數競爭攻擊。就像在重放攻擊中所說,DAO合約不能正確的更新合約狀態,并且可以讓資金被盜竊。DAO問題和外部調用中的部分原因是跨函數競爭條件攻擊的潛在原因。雖然以太坊中所有的轉賬是線性發生(一個在另一個后面), 外部調用(另一個合約或者地址的調用)如果沒有被合理管理,就會成為災難的導火線。在現實世界中,他們是完全可以避免的。當兩個函數被調用并且分享同個狀態,跨函數競爭條件攻擊就會發生。這個合約就會想到,現在有兩個合約狀態存在,但是現實是只有一個真正的合約狀態存在。我們不能同時獲得X = 3和X = 4這兩種結果。 讓我們用一個例子來說明這個內容。Y55顯卡之家

攻擊和代碼Y55顯卡之家

contract crossFunctionRace{Y55顯卡之家

mapping (address => uint) private userBalances;Y55顯卡之家

/* uses userBalances to transfer funds */Y55顯卡之家

function transfer(address to, uint amount) {Y55顯卡之家

if (userBalances[msg.sender] >= amount) {Y55顯卡之家

userBalances[to] += amount;Y55顯卡之家

userBalances[msg.sender] -= amount;Y55顯卡之家

Y55顯卡之家

Y55顯卡之家

/* uses userBalances to withdraw funds */Y55顯卡之家

function withdrawalBalance() public {Y55顯卡之家

uint amountToWithdraw = userBalances[msg.sender];Y55顯卡之家

require(msg.sender.send(amountToWithdraw)());Y55顯卡之家

userBalances[msg.sender] = 0;Y55顯卡之家

Y55顯卡之家

Y55顯卡之家

上面的合約有2個功能 – 一個是可以轉移資金,另一個是提現資金。我們假設攻擊者調用了函數transfer(),然后同時使用外部調用函數withdrawalBalance()。userBalance[msg.sender]的狀態通過2個不同的方向被抽出。用戶的余額還沒有被設為0,但是盡管資金已經被提取,攻擊者也能夠轉移資金。這樣情況下,合約可以讓攻擊者使用雙花,這也是區塊鏈技術想要解決的問題之一。Y55顯卡之家

注意:如果有函數分享狀態,跨函數競爭條件攻擊就會在多個合約中發生。Y55顯卡之家

-在調用外部函數之前,應該完成所有的內部工作Y55顯卡之家

-避免發生外部調用Y55顯卡之家

-在不可避免地時候,使用外部函數“不可信”Y55顯卡之家

-在外部調用不可避免的情況下,使用互斥Y55顯卡之家

根據下面的合約,我們可以看到一個例子1) 在完成外部調用之前,完成內部工作。2)將所有外部調用都設為“不可信”。我們的合約會讓資金發送到一個地址,并且允許用戶一次性將資金存入合同。Y55顯卡之家

contract crossFunctionRace{Y55顯卡之家

mapping (address => uint) private userBalances;Y55顯卡之家

mapping (address => uint) private reward;Y55顯卡之家

mapping (address => bool) private claimedReward;Y55顯卡之家

//makes external call, need to mark as untrustedY55顯卡之家

function untrustedWithdraw(address recipient) public {Y55顯卡之家

uint amountWithdraw = userBalances[recipient];Y55顯卡之家

reward[recipient] = 0;Y55顯卡之家

require(recipient.call.value(amountWithdraw)());Y55顯卡之家

Y55顯卡之家

//untrusted because withdraw is called, an external callY55顯卡之家

function untrustedGetReward(address recipient) public {Y55顯卡之家

//check that reward hasn’t already been claimedY55顯卡之家

require(!claimedReward[recipient]);Y55顯卡之家

//internal work first (claimedReward and assigning reward)Y55顯卡之家

claimedReward = true;Y55顯卡之家

reward[recipient] += 100;Y55顯卡之家

untrustedWithdraw(recipient);Y55顯卡之家

Y55顯卡之家

Y55顯卡之家

我們可以看出,這個合約的首個函數在發送資金到用戶的合約/地址的時候,就會發生外部調用。同樣地,獎勵函數在發送一次性獎勵的時候,也會使用提現函數,因為這也是不可信的。同樣重要地是,合約需要執行所有內部工作。就好像重入攻擊,函數untrustedGetReward()會在允許提現之前,讓用戶獲得一次性的獎勵,從而防止跨函數競爭條件攻擊。Y55顯卡之家

在真實世界,智能合約不需要依賴于外部調用。事實上,外部調用在很多情況下,在工作環境中都幾乎不可能發生的。由于這個原因,使用互斥體來“鎖定”一些狀態,并且讓擁有者有能力去改變狀態,可以幫助防止這類災難。雖然互斥體非常有效,但是當用于多個合約的時候,都會變的很棘手。如果你使用互斥體來防止這類攻擊,你需要很仔細地確保沒有其他方法來鎖定,或者永遠不會釋放。如果使用互斥體的方法,在寫入智能合約的時候,你需要保證你完全理解潛在的危險。Y55顯卡之家

contract mutexExample{Y55顯卡之家

mapping (address => uint) private balances;Y55顯卡之家

bool private lockBalances;Y55顯卡之家

function deposit() payable public returns (bool) {Y55顯卡之家

/*check if lockBalances is unlocked before proceeding*/Y55顯卡之家

require(!lockBalances);Y55顯卡之家

/*lock, execute, unlock */Y55顯卡之家

lockBalances = true;Y55顯卡之家

balances[msg.sender] += msg.value;Y55顯卡之家

lockBalances = false;Y55顯卡之家

return true;Y55顯卡之家

Y55顯卡之家

function withdraw(uint amount) payable public returns (bool) {Y55顯卡之家

/*check if lockBalances is unlocked before proceeding*/Y55顯卡之家

require(!lockBalances && amount > 0 && balances[msg.sender]Y55顯卡之家

>= amount);Y55顯卡之家

/*lock, execute, unlock*/Y55顯卡之家

lockBalances = true;Y55顯卡之家

if (msg.sender.call(amount)()) {Y55顯卡之家

balances[msg.sender] -= amount;Y55顯卡之家

Y55顯卡之家

lockBalances = false;Y55顯卡之家

return true;Y55顯卡之家

Y55顯卡之家

Y55顯卡之家

以上,我們可以看到合約mutexExample()會有私人鎖定狀態,來實行deposit()函數功能和withdraw()函數。鎖定會防止用戶能夠在所有的初步調用完成之前,成功完成withdraw()調用,可以防止任何種類的跨函數競爭條件攻擊。Y55顯卡之家

最后的結果Y55顯卡之家

力量越大,責任越大。雖然區塊鏈和智能合約技術每天都在革新,但是風險依然很高。攻擊者從沒有放棄去尋找機會來攻擊這些合約。這取決于我們來保證,我們可以從之前項目的問題中學習經驗,來讓我們獲得成長。希望通過這篇文章,以及其他系列文章,你可以更明白智能合約攻擊。Y55顯卡之家

責任編輯:ctY55顯卡之家

相關推薦