天天看點

從安全角度看 Move 語言特性與可能存在的漏洞

此前,Beosin 宣布了全新更新的安全審計服務,而現在,Beosin 安全團隊正式宣布推出針對 Move 智能合約的安全審計服務,旨在提前發現并協助項目方修複項目中的安全風險,保障使用者與項目方的資産安全。

從安全角度看 Move 語言特性與可能存在的漏洞

1、基礎概念

Move 語言最初是由 Facebook 團隊為 Diem(原名 Libra)區塊鍊而設計開發的一門新語言。而 Libra 的使命是打造一個簡單的全球貨币和金融基礎設施,為數十億人提供支援。Move 語言旨在提供一個安全、可程式設計的基礎,可以在此基礎上建構這一願景。Move 必須能夠以精确、可了解和可驗證的方式表達 Diem 貨币和治理規則。從長遠來看,Move 必須能夠對構成金融基礎設施的各種資産和相應的業務邏輯進行編碼。

那麼,如何實作這一願景呢?在 Move 的白皮書中提出了設計時考慮的四個關鍵目标:first-class 資産、靈活性、安全性和可驗證性。其中 first-class 資産是實作這一願景的基礎。要了解 first-class 資産,我們先來介紹 Move 語言中的幾個比較重要的基礎概念。

1.1 結構體

和其他很多語言一樣,Move 語言中的結構體也是使用 struct 定義。它是自定義類型,也是 Move 中建立自定義類型的唯一方法。結構體可以包含複雜資料,也可以不包含任何資料,但不允許定義遞歸結構體。結構體由字段組成,可以簡單地了解成「key-value」存儲,其中 key 是字段的名稱,而 value 是存儲的内容。

1.2 能力

Move 的類型系統非常靈活,每種類型都可以被四種限制符所修飾。這四種限制符我們稱之為 abilities,它們的功能分别是:

  • Copy - 被修飾的值可以被複制。
  • Drop - 被修飾的值在作用域結束時可以被丢棄。
  • Key - 被修飾的值可以作為鍵值對全局狀态進行通路。
  • Store - 被修飾的值可以被存儲到全局狀态。

而 Move 的基本類型預設具有 store, copy 和 drop 限制,自定義類型預設情況下結構體不帶任何限制符。

1.3 資源

介紹完了結構體和能力,接下來就是 Move 語言中的核心概念:資源(resource)。

如果定義的結構體(struct)不能複制(copy)也不能丢棄(drop),我們就将它們稱為資源。預設情況下,結構體是線性和短暫的。它們不能複制,不能丢棄,不能存儲在全局存儲中。這意味着所有值都必須擁有被轉移的所有權,并且值必須在程式執行結束時處理。我們可以通過賦予結構體能力(abilities)來簡化這種行為,允許值被複制或删除,以及存儲在全局存儲中或定義全局存儲的模式。

Move 語言中的這個屬性對于現實世界中資源(例如貨币)的模組化非常有用,因為貨币在理論上是不希望在流通過程中被複制或丢失的。

1.4 子產品

Move 語言中,代碼都是以子產品(module)的形式進行組織的。如何了解子產品呢?抽象的了解,Move 語言中的子產品(module)/ 資源(resource)/ 方法(function)之間的互動與傳統的面向對象語言中的類(class)/ 對象(object)/ 方法(function)之間的關系非常相似。

而與其他區塊鍊語言相比,Move 中的子產品類似于其他區塊鍊中的智能合約。開發者在子產品中聲明資源類型和方法,這些類型和方法定義了建立、銷毀和更新已聲明資源的規則。

1.5 泛型

Move 因為是靜态語言,是以為了保證擴充性,Move 選擇了泛型程式設計的範式。

以 Aptos 為例,隻要檢視你持有的資産是 0x1::coin::CoinStore 這樣的類型,就可以知道該資産是由标準模組 0x1::coin 所定義。比如說 Aptos 的原生币 AptosCoin 的類型是 0x1::aptos_coin::AptosCoin ,意思是 0x1 帳号之下的 aptos_coin 子產品所定義的 AptosCoin 類型,不過這個類型并沒有賦予 key 能力,是以不能成為資源,他隻提供一個種類,而這個類型可以作為泛型來使用,像是被 0x1::coin::CoinStore 使用。

那麼,0x1::coin::CoinStore<0x1::aptos_coin::AptosCoin> 就是 0x1 帳号下 coin 子產品所定義的 CoinStore 類型,記錄位址下某種 coin 的資産狀态。是以你的位址下任何 0x1::coin::CoinStore 的資産都通用相同邏輯、有一樣的行為,都是定義在 0x1::coin 這個子產品裡。

2. 安全性

2.1 設計安全

Move 在白皮書中明确表示,Move 必須拒絕不滿足關鍵屬性的程式,例如 Resource 安全、類型安全和記憶體安全。我們如何選擇一個可執行的表示,以確定在區塊鍊上執行的每個程式都滿足這些屬性?兩種可能的方法是:

(a) 使用帶有檢查這些屬性的編譯器的進階程式設計語言

(b) 使用低級無類型彙編并在運作時執行這些安全檢查。

Move 采取了介于這兩個極端之間的方法。Move 的可執行格式是一種類型化的位元組碼,它比彙編進階,但比源語言低。位元組碼由位元組碼驗證器在鍊上檢查 Resource、類型和記憶體安全性,然後由位元組碼解釋器直接執行。這種選擇允許 Move 提供通常與源語言相關的安全保證,但無需将源編譯器添加到受信任的計算庫或将編譯成本添加到交易執行的關鍵路徑中。

除此之外,Move 在設計時内置了很多安全特性,例如算數溢出、預設可見性導緻的權限洩露等等。

2.2 底層安全

2.2.1 資源

在現實社會中,資産有兩個屬性難以用數字表示:

1)稀缺性。必須控制系統中資産發行的數量。必須禁止複制現有資産,而建立新資産是一項特權操作。

2)通路控制。系統參與者必須能夠使用通路控制政策保護資産。

是以,Move 引入了資源來表示資産。而在區塊鍊應用中,代币就是一種資源,資源必須要存儲在賬戶下面,且在交易過程中,資産必須要流向一個地方,要麼轉移到另一個位址,要麼被銷毀,代币不可被複制或被使用多次或被「懸挂(dangling)」。此處可以把資源的操作類比成 c++ 中的唯一指針。

2.2.2 先位元組驗證,後合約執行

Move 在設計時就設計為一種可執行的位元組碼語言,其具有内置的安全算法和位元組碼驗證器(Bytecode verifier),可以防止許多常見錯誤。即 Move 中的合約代碼要能被執行,必須先被驗證,這使得合約可以免受編譯器的潛在故障和可能遭遇到的攻擊。Krešimir Klas 在其《Smart Contract Development — Move vs. Rust》中表示:「Move 的顯著特征是可執行的位元組碼表示,為所有程式提供了資源安全保證。考慮到合約的開放部署模型,這一點至關重要——回想一下,任何合約都必須容忍與不可信代碼進行任意互動。如果源碼級線性可以被可執行級别上不受信任的代碼違反,那麼其價值就很有限。」

2.2.3 靜态調用

在區塊鍊中,合約的調用方式可以分為靜态調用和動态調用。若程式調用必須在運作時才能确定被調用的目标,則稱該調用為動态調用;反之,在運作前即可确定被調用目标,且在運作時無法變更該目标,則稱該調用為靜态調用。

Move 采用靜态調用方式。即 Move 實作的合約在部署時,其執行邏輯已經被确定。那麼我們可以通過靜态分析位元組碼,得到合約所有可能路徑上操作的狀态,在區塊浏覽器或錢包裡提示給使用者。

是以,錢包提供商可以在錢包設計中,在預執行合約時把合約執行後的狀态變更提示給使用者,讓使用者可以知道這個交易操作了自己的哪些重要資産,以及執行後的結果。如下圖,在 StarMask 中的實作效果:

從安全角度看 Move 語言特性與可能存在的漏洞

圖源自 jolestar.eth

3. Move 與 Solidity 的比較

3.1 賬戶模型

Solidity:

在大多數以太坊 ERC-20 合約中,每個位址的餘額都存儲在一個類型的狀态變量 mapping(address => uint256) 中,該狀态變量存儲在特定智能合約的全局存儲中。其結構如下圖所示:

從安全角度看 Move 語言特性與可能存在的漏洞

圖源:https://github.com/move-language/move/tree/main/language/documentation/tutorial

Move:

Move 中,類比 solidity 智能合約的子產品瑟吉歐沒有自己存儲空間的。相反,Move 的「全局存儲」是由位址索引的,每個位址下存儲了 Move 子產品和 Move 資源,而資源存儲是類型到值的映射。其結構如下圖所示:

從安全角度看 Move 語言特性與可能存在的漏洞

圖源:https://github.com/move-language/move/tree/main/language/documentation/tutorial

3.2 代碼存儲

Solidity:

在基于 EVM 的鍊中,所有智能合約都有一個獨特的位址,稱為「合約擁有位址」。合約賬戶與部署者賬戶(EOA)存在于在同一級别,代碼 hash 存儲在合約賬戶位址中,合約部署後不與部署者位址綁定。

從安全角度看 Move 語言特性與可能存在的漏洞

Move:

在基于 MoveVM 的鍊中,代碼存儲在 Account resource 的 code module 裡面。

從安全角度看 Move 語言特性與可能存在的漏洞

3.3 安全隔離

Solidity:

智能合約的運作環境是鍊的節點給構造出的沙箱環境,多個合約程式是運作在同一個程序内的不同的虛拟機沙箱。智能合約之間的調用是同一個程序内不同的智能合約虛拟機之間的調用,安全完全依賴于智能合約虛拟機之間的隔離。

從安全角度看 Move 語言特性與可能存在的漏洞

Move:

Move 的做法則是通過 MoveVM 讓采用 Move 語言的區塊鍊具備确定性,将合約調用放在同一個虛拟機沙盒中,通過程式設計語言内部的安全性對智能合約的狀态進行隔離,而非依賴虛拟機進行隔離。

從安全角度看 Move 語言特性與可能存在的漏洞

3.4 合約更新

Solidity:

EVM 中合約更新的方法是将合約資料和邏輯分析:代理合約(Proxy)負責轉發交易到邏輯合約,并儲存合約資料;邏輯合約(Logic)負責實作功能邏輯。更新時,隻需要重新部署新版本的邏輯合約,并将代理合約中的邏輯合約執行個體指向新版本邏輯合約執行個體即可。此時,邏輯合約更新并不會影響合約原來已有的資料。

如下圖,代碼存儲字段指定的是代理能合約被調用時做 delegate call 的合約代碼,合約更新本質上是部署一個新的邏輯合約,并改變 code 字段以重定向 delegate call。

從安全角度看 Move 語言特性與可能存在的漏洞

Move:

Move 語言中對于合約更新,其實作是在系統子產品 code.move 中執行更新邏輯,在代碼部署前檢查更新政策和相容性。在相容性檢查後,寫在 resource 中的代碼通過一個原生函數調用被替換,并将執行新的邏輯。

從安全角度看 Move 語言特性與可能存在的漏洞

4. Move 程式可能出現的漏洞點

1) 開發者在使用 Aptos、Sui,或者其他基于 Move 的 blockchain 中獨特的 Framework 進行開發時,應保持一定程度的安全意識,確定供應鍊安全。

2) 函數權限問題。對于一些函數調用的權限要仔細劃分,因為一些關鍵函數會涉及到治理,嚴重的會影響到資金安全,針對這種函數調用需要對調用者進行鑒權。

3) 業務邏輯在設計和代碼實作時,均需注意其中的邏輯問題。

4) 關于 Move 系項目,在子產品更新時需要注意的點:

  • 根據合約更新政策,代碼的所有者對更新權限有完全的控制權。
  • 代碼的 owner 在初始部署後是不可改變的。
  • 部署者的位址在部署後永遠擁有更新權限。

繼續閱讀