針對區塊鏈安全問題,成都鏈安科技團隊每一周都將出智能合約安全漏洞解析連載,希望能幫助程序員寫出更加安全牢固的合約,防患於未然。

引子:外以欺於人,內以欺於心 – 唐·韓愈《原毀》

前情提要

上回書,轉賬過程紛繁複雜,安全應對各個擊破。

面對直接涉及以太轉賬的遊戲合約, 在使用官方提供的轉賬函數同時,添加不同賬戶類型的區別處理以及失敗情況下的異常處理乃明智之舉。此外,構建合約邏輯避免依賴於合約餘額確切值應銘記在心,如有特殊考慮,切記定義狀態變數明確餘額變化,萬不可想當然而為之。

本期話題

第九回, 合約安全隱私未必,外部讀取暴露無遺。

我們在前幾回主要討論的都是在合約內部構建函數和代碼書寫規範時產生的一些誤區和安全隱患。但是對於智能合約這個嶄新的概念,僅僅從合約層面本身考慮合約的安全可能並不足夠。合約開發者在這個產業蓬勃發展的過程當中,為了趕上項目進度,不得以邊學習邊實踐,即使在代碼書寫層面功底很紮實的情況下,仍然會因為區塊鏈技術的特殊性埋藏一些不自知的安全隱患。

所以,這一回我們將要闡述的安全隱患就是因為「合約中的一切都是公開可見」這一特性造成的。

基礎小知識

在第七期的關於未初始化局部變數中我們介紹了變數存儲的知識。Solidity對複雜的數據類型,比如數組和結構體,會默認存儲在Storage當中。

沒有提到的是關於智能合約中對於變數從作用域的劃分。目前的劃分包括三種,全局公有變數,全局私有變數,局部變數。

其中,全局變數一般儲存在storage當中,而結構、數組或映射類型的局部變數,默認會放在 存儲storage 中,除結構、數組及映射類型之外的局部變數,會儲存在棧中。

公有(public)和私有(private)是可見性說明符(Visibility Specifier),公有變數在合約內部外部均可見,而私有變數僅在當前合約可見(可調用)。

另外還有兩個可見性類別在這裡我們也介紹一下:external,表示僅在外部可見,即僅可用於消息調用;internal,僅在內部可見,表示的意思是僅在Solidity合約與子合約均可見,不僅限於當前合約內。

問題出在哪

由於狀態變數一般是需要永久存儲的變數,所以一定會儲存在storage中,聯繫第七期的知識,storage是存在於區塊鏈當中的,可以類比為計算機的存儲磁碟。那麼狀態變數其實一直存在於區塊鏈當中,對於結構、數組或映射類型的局部變數也是同樣的道理。

這樣一來,即使加上了可見性說明符,例如私有狀態變數,根據區塊鏈公開的特性,它也還會是公開的,因為這個私有僅限於合約層面的私有,合約之外依然可以讀取。

這個附加的可見性說明符就像騙子給皇帝穿上的新衣,可以「騙」到合約層面的函數和變數,在合約外部卻形同虛設。

那麼,從外部如何讀取狀態變數呢,下面我們從合約外部的角度分析具體的流程。

如何從合約外部讀取變數

一、 狀態變數

接著上面的概念,合約的狀態變數都是存在於區塊鏈中,就像存儲在磁碟中的文件,因此,那麼我們可以直接通過訪問區塊鏈獲取這些狀態變數的值。

例如針對以下案例合約進行全真模擬操作:

a. 將上面的合約部署在ropsten測試鏈上,獲得地址0x9e550E6911b38412964C5C956383757c9FA7F860,

b. 然後登陸METAMASK錢包

c. 進入瀏覽器控制台console:

1. 查看變數a,輸入:

web3.eth.getStorageAt(0x9e550E6911b38412964C5C956383757c9FA7F860, 0,function(x,y){console.log(y)})

會得到:

2. 查看變數str

獲取16進位結果,輸入:

web3.eth.getStorageAt(0x9e550E6911b38412964C5C956383757c9FA7F860, 1,function(x,y){console.log(y)})

轉換為字元串,輸入:

web3.eth.getStorageAt(0x9e550E6911b38412964C5C956383757c9FA7F860, 1,function(x,y){console.log(web3.toAscii(y))})

會得到

3. 查看owner

web3.eth.getStorageAt(0x9e550E6911b38412964C5C956383757c9FA7F860, 2,function(x,y){console.log(y)})

4. 查看balanceOf[owner]

這個稍微複雜一些,由於映射變數不是按照定長變數的順序存儲,其是一個鍵值對,EVM採用的機制是將其存在sha3(key+slot)處

輸入進控制台,得到:

二、 局部變數

局部變數直接編碼到opcode中,例如上面合約localVar函數中的local變數

三、 建議:

上面的讀取操作驗證了只要是儲存在storage裡面的變數,都是可以通過不執行合約函數就直接獲取的。

因此,依賴可見性說明符,將隱私,不可公開的數據存儲在區塊鏈上是很不安全的做法。

如果真的要將隱私數據儲存在區塊鏈中,也要進行相應的加密處理。

欲窮千里目,更上一層樓

區塊鏈時代的安全包含傳統互聯網安全,智能合約安全等多個方面,對於合約層面的安全考量固然是一方面,但是對於數據存儲方面,不沿用互聯網安全的理念,盲目信賴新技術卻又不深入挖掘新技術某些特性、限制條件、特殊情況,勢必無法做到真正意義上的全面防護。目前區塊鏈產業步伐放緩,正是我們深入學習了解這個領域的最佳時機,也是這個產業提升穩健性的最佳契機。更上層樓,與君共勉。

參考資料:

[1]: 以太坊智能合約OPCODE逆向之理論基礎篇

以太坊智能合約 OPCODE 逆向之理論基礎篇?

paper.seebug.org
圖標

[2]:可見性和getter函數:

合約 - Solidity develop 文檔?

solidity-cn.readthedocs.io

[3]: JSON-RPC#eth_getstorageat

https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getstorageat?

github.com
圖標

[4]: 以太坊數據存儲的思考和解讀

聽說懂以太坊開發的程序員都被搶瘋了!分享一篇價值10萬的文章,來自10年經驗的大咖對以太坊數據存儲的思考與解讀 - 區塊鏈大本營 - CSDN博客?

blog.csdn.net
圖標

[5]: How to read Ethereum contract storage

https://medium.com/aigang-network/how-to-read-ethereum-contract-storage-44252c8af925?

medium.com


推薦閱讀:

相关文章