Solidity
即使我在同一函式中將餘額設置為零,該函式是否容易受到重入攻擊(包括範例)
例如在這種情況下,是否可以通過重入來破解?謝謝…
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
- 部署受害者
- 給受害者一些錢,
Victim.deposit()
- 使用受害者的地址部署攻擊者
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 }
希望能幫助到你。