作者:Artela 中文博客,medium
摘要
重入攻擊仍然是一個挑戰,現有的防御手段主要集中在協議源代碼層面,僅在合約進入runtime 狀態前生效
「運行時保護」是DeFi 安全的重要補充,它以「保護執行結果」為目的,確保協議的執行與其預期設計一致
EVM 的設計不支持「運行時保護」,因為智能合約無法訪問runtime 狀態全部上下文信息
Artela 探索一種EVM+Extension 的執行層範式,增強執行層以消除重入攻擊
Artela 通過Aspect Programming 實現鏈上「運行時保護」擴展
我們逐步展示瞭如何通過Aspect 防範對Curve 合約的重入攻擊
為什麼儘管存在風險控制措施,重入攻擊仍然是一個挑戰
儘管重入攻擊是一個眾所周知的問題,並且出現了許多風險控制措施,但在過去的兩年中,涉及此類攻擊的安全事件仍在不斷發生:
Curve Finance 攻擊(2023 年7 月)—6000 萬美元,Curve 因其合約編程語言Vyper 編譯缺陷遭受重入攻擊。
Origin Protocol 攻擊(2022 年11 月)—700 萬美元,穩定幣項目Origin Dollar(OUSD)遭受了重入攻擊。
Siren Protocol 攻擊(2021 年9 月)—350 萬美元,AMM 池遭受重入攻擊。
Cream Finance 攻擊(2021 年8 月)—1880 萬美元,攻擊者利用重入漏洞進行二次借貸。
目前,防範重入攻擊的重點集中在智能合約的源代碼層面,措施包括集成OpenZeppelin 的ReentrancyGuard,以及對合約邏輯代碼進行安全審計,以避免預定義的安全隱患。
這種方法被稱為「白盒」解決方案,旨在在源代碼層面規避漏洞,以最小化邏輯錯誤。然而,其主要挑戰在於無法防禦未知隱患。
將合約從源代碼「轉化」為實際runtime 是個具有挑戰性的過程。每一步可能為開發人員帶來無法預料的問題,而合約源代碼本身可能無法全面涵蓋所有潛在情況。在Curve 的案例中,由於編譯器問題,即使協議源代碼是正確的,最終運行結果與協議的預期設計之間仍可能存在差異。
僅僅依靠協議在源代碼和編譯層面的安全性是不足夠的。即使源代碼看起來毫無瑕疵,由於編譯器問題,漏洞仍可能意外出現。
我們需要運行時保護
與現有的風險控制措施集中在協議源代碼層面並在運行之前生效不同,運行時保護涉及協議開發人員編寫運行時保護規則和操作,以處理運行時的未預料情況。這有助於對運行時執行結果進行實時評估及應對。
運行時保護在增強DeFi 安全性方面至關重要,是現有安全措施的重要補充。通過以「黑盒」方式保護協議,它通過確保最終運行結果與協議預期設計相一致來增強安全性,而無需直接干涉合約代碼執行。
實施運行時保護的挑戰
不幸的是,EVM 設計不支持在鏈上實現運行時保護,因為智能合約無法訪問完整的運行時上下文。
如何克服這一挑戰?我們認為以下先決條件是必要的:
一個專門的模塊,可以訪問跨智能合約的所有信息,包括整個交易上下文。
從智能合約獲得必要的授權,使模塊有權根據需要回撤(revert) 交易。
確保模塊的功能在智能合約執行後和狀態提交之前生效。
EVM 在解決上述挑戰中目前面臨限制,難以容納更多創新。在模塊化區塊鏈的範式下,執行層需要探索go beyond EVM 的突破。
Artela 的思路是EVM + native extension,通過構建EVM 的WASM 原生擴展層以實現go beyond EVM。
Aspect Programming 介紹
我們推出了Aspect Programming,這是支持Artela 區塊鏈的一種編程框架,支持在區塊鏈上進行原生擴展。
Aspect 是可編程的原生擴展模塊,用於在運行時動態集成自定義功能到區塊鏈中,作為智能合約的模塊化補充,增強鏈上功能性。
Aspect 的特性是能夠訪問區塊鏈基礎層的系統級API,並在交易生命週期的各個切點(Join Point)添加擴展邏輯。智能合約可以綁定指定的Aspect 以觸發擴展功能。當交易調用智能合約時,該交易也會經由與該合約關聯的Aspect 處理。
Aspect Programming 如何實現運行時保護
Aspect 可以記錄每個函數調用的執行狀態,並防止在回調函數執行期間發生重入。當在回調函數執行期間發生重入調用時,Aspect 會檢測到並立即回撤該交易,防止攻擊者利用重入漏洞。通過這種方法,Aspect 有效地消除了重入攻擊,確保智能合約的安全性和穩定性。
Aspect 實現運行時保護的關鍵屬性:
可在智能合約執行後和狀態提交前觸發:Aspect 模塊可設置為在智能合約執行後但在狀態提交前激活。
完整的交易上下文訪問:Aspect 可以訪問完整的交易上下文,包括整個交易信息(方法,參數等)、調用棧(執行過程中所有內部合約調用)、狀態上下文變更以及所有交易觸發的事件。
系統調用能力:Aspect 可以進行系統調用,並在必要時發起交易回撤。
與智能合約的綁定和授權:智能合約可以綁定到Aspect,並授予Aspect 參與交易處理的權限。
實施Aspect 進行重入保護
本章我們探討如何在鏈上實現Aspect 的運行時保護。
可以在「preContractCall」和「postContractCall」的切點(Join Point)中部署一個實際的「合約保護意圖」Aspect,以防止重入攻擊。
preContractCall: 在跨合約調用執行之前觸發
postContractCall: 在跨合約調用執行後觸發
為進行重入保護,我們的目標是在調用結束之前阻止合約重入。通過Aspect,我們可以通過在交易生命週期的切點處添加特定邏輯來實現這一目標。
在「preContractCall」切點中,Aspect 監控合約調用堆棧。如果在調用堆棧中有任何重複調用(這意味著我們鎖定的調用中出現了意外重入),Aspect 將會回撤該調用。
部署Curve 合約並防範重入攻擊
我們編寫了Curve 模擬合約並複刻重入攻擊,以更易理解的方式重現了這個過程。合約代碼如下:
可以看到,上述合約的add_liquidity 和remove_liquidity 都由同一個重入鎖lock 進行了保護,這意味著如果重入保護正常工作,無法通過改鎖重入被保護函數(例如,在remove_liquidity 中調用add_liquidity) 。
使用vyper 編譯器0.2.15、0.2.16 或0.3.0(這些版本存在已知的重入保護問題)編譯上述合約。
然後,我們部署上述受害者合約,並使用以下合約對其進行攻擊:
模擬實際攻擊,此合約的attack 方法嘗試通過其fallback 函數從remove_liquidity 方法重入add_liquidity。如果實際發生了重入,可在receipt 中觀察到在RemoveLiquidity 事件之前記錄了一個AddLiquidity 事件。
現在讓我們使用Aspect 來保護受攻擊的合約。在執行以下操作之前,請先完成以下步驟:
1、部署Aspect
2、將受害合約與Aspect 綁定
如果對Aspect 操作不熟悉,可以首先查看我們的開發者指南先行了解。
完成上述操作後,現在讓我們嘗試再次調用attack 方法,以檢查操作是否會成功進行。
從動圖(文末原文鏈接可查看)中我們可以看到,重入交易已經被revert,這意味著我們的Aspect 正在成功保護受害合約免受重入攻擊。
結論
最近對Curve 的攻擊再次說明了沒有100% 完全安全的協議。僅僅將重心放在協議的源代碼和編譯級別的安全性上是不足夠的。即使源代碼看起來毫無瑕疵,由於編譯器問題,漏洞仍然可能意外出現。
為了增強DeFi 的安全性,運行時保護變得至關重要。通過以「黑盒」方式保護協議,確保協議的執行與其預期設計一致,可以有效地防止運行時的重入攻擊。
我們復刻了Curve 合約並完全模擬了其近期的重入攻擊,並以更易理解的方式再現了整個過程。利用Aspect 編程作為一種新方法,實現鏈上運行時保護,我們逐步展示瞭如何用Aspect 保護受害合約。我們的目標是幫助根除Curve 等DeFi 協議可能遭受的重入攻擊,從而增強整個DeFi 領域的安全性。
通過Aspect Programming,開發人員不僅可以在安全領域實現鏈上運行時保護,還能實現諸如意圖、JIT 和鏈上自動化等前所未有的創新用例。除此之外,這個以Cosmos SDK 為基礎的通用框架將不僅支持Artela 區塊鏈,更能支持其他區塊鏈構建基於自己執行層的native extension。