Solidity

即使我在同一函式中將餘額設置為零,該函式是否容易受到重入攻擊(包括範例)

  • February 23, 2019

例如在這種情況下,是否可以通過重入來破解?謝謝…

function withdraw(uint256 money) public {
   if(money <= balance[msg.sender]) {
       (bool success, ) = msg.sender.call.value(money)("");
       if(success) {
           balance[msg.sender] -= money;
       } 
   }

是的。

在將流量控制轉移到您不信任的契約之前,您必須整理好狀態。msg.sender可以是任何人/任何東西,所以這適用。

您已將所有氣體發送到備份功能。它可以像這樣循環:

contract Attacker {

 // ... set up the victim contract and other details, then ...

 function () payable {
   // check available gas
   // if sufficient gas for another run
   if(sufficientGas) {
      victim.withdraw(amountToStealOnThisIteration); // nothing stops this and balance[msg.sender] is not updated yet
   } // only now that the attack is over will msg.sender's balance be adjusted in the victim contract
 }

}

我不確定它是否特別清楚,但每次呼叫你的函式時,這個備份函式msg.sender都會接管。這是流量控制的轉移。它可以環繞並再次呼叫您的函式(再次給我我的錢),然後再次**呼叫。當然,如果氣體耗盡,攻擊將失敗,但請記住,攻擊者可以練習這一點,直到它順利執行。

這是一個非常簡單(和舊)的要點:https ://gist.github.com/rob-Hitchens/7eab95883e1d30b7224f024304a1f713

  1. 部署受害者
  2. 給受害者一些錢,Victim.deposit()
  3. 使用受害者的地址部署攻擊者
  4. Attacker.attack()

這將花費大約 30 倍於預期的資金。

您可以通過樂觀會計輕鬆解決此問題。換句話說,更新狀態,就好像確保成功一樣。如果 !success,則恢復(require()執行),它將恢復之前的狀態更改。

function withdraw(uint256 money) public {
   uint bal = balance[msg.sender];
   balance[msg.sender] =- money; // optimistic accounting
   require(money <= bal]); // fail hard
   require(msg.sender.call.value(money)("")); // If the returned bool isn't true then fail hard
   // we are done
}

希望能幫助到你。

引用自:https://ethereum.stackexchange.com/questions/67507