重入攻擊的事務時間含義
這是經典的再入攻擊:
function withdrawBalance() public { uint amountToWithdraw = userBalances[msg.sender]; (bool success, ) = msg.sender.call.value(amountToWithdraw)(""); require(success); userBalances[msg.sender] = 0; }
我不明白這是如何工作的。似乎一項事務未在另一項之前完成。我知道在 Dao 攻擊中,使用 call.value() 上的回退函式的合約在調整狀態變數 userBalances 之前操縱系統停止
$$ msg.sender $$,但如果這僅僅是由於通過此回退函式創建的額外時間,則表明這是一個不太可能但可能存在的問題。 假設我在 contractBalance 中有 10 個代幣,使用者可以將它們放入他們的合約餘額中。
function takeAllTokens(uint x) public { require(x < contractBalance); uint amountToTake = contractBalance; uint y = x + 1; // just to give it a couple pico seconds contractBalance -= x; userBalances[msg.sender] = contractBalance; }
如果交易沒有完全按順序處理,兩個同時到達並要求 10 個代幣的人最終將各自獲得 10 個代幣,比本範例中的 contractBalance 中的要多。
這僅僅是不可能的,因為在後一種情況下,從一條線到另一條線所需的時間是如此之短,以至於雖然可能,但它的機率被認為是不可能的?
我將嘗試以希望有所幫助的方式解決一些誤解。
這並不像你認為的那樣。
uint y = x + 1; // just to give it a couple pico seconds
關於時間時間的直覺想法並不適用。考慮現在和將來(同步)每個節點如何冗餘執行事務。時間上的開始時間是什麼時候?這不是真的可以定義,是嗎?持續時間相同。如果它可以被測量,它在每個節點上都會有所不同,並且它不是確定性的,因此不是合約可以訪問的東西。
雖然在物理世界中顯然不是這樣,但合約功能和交易應該在邏輯上被理解為即時執行。
沒有並行性,也沒有兩個事務同時執行的可能性。它們在塊中排序,並且每個都在前一個已完成事務的上下文中執行 - 單執行緒順序執行。
塊中的交易按順序在塊時間戳指示的時間時間上或附近即時執行……當發現塊時。
重入與時間無關。它是關於流控制的轉移。易受攻擊的合約期望流量控制恢復,但它可能不會。已獲得流量控制的合約可能會在最終返回之前進行惡作劇。這可能會導致各種邏輯錯誤,因為攻擊者可能能夠在合約處於不一致狀態時呼叫任何函式。
防禦是把所有的驗證放在首位,然後根據需要更新狀態,最後(總是最後)允許流控制傳遞給另一個合約。首先把房子整理好會阻止大多數詭計。
希望能幫助到你。