iT邦幫忙

2023 iThome 鐵人賽

DAY 25
0
Web 3

Web3 X 公共財系列 第 25

Day 25 Hypercert audit 報告

  • 分享至 

  • xImage
  •  

Audit

在contracts/audit 共計有三分審計報告

1. 20230201-7naly3er

a. Gas優化的建議

編號 問題 數量
GAS-1 使用組合語言檢查 address(0) 1
GAS-2 使用布爾值作為存儲會產生開銷 1
GAS-3 對於不會變動的函數參數,使用calldata而不是memory 8
GAS-4 對於不會溢出的操作,您可以使用unchecked 102
GAS-5 確定在被普通用戶調用時會還原的函數可以標記為payable 7
GAS-6 使用private而不是public來保存常量,可以節省gas 1
GAS-7 使用 != 0 而不是 > 0 進行無符號整數比較 1
GAS-8 未被合約調用的internal函數應該被移除

補充:
GAS-3 對於不需要變動的函數參數,使用calldata而不是memory

在可能的情況下,將資料類型標記為calldata而不是memory。這樣可以避免資料自動被加載到記憶體中。如果傳入函數的資料不需要被改變(例如在陣列中更新值),它可以作為calldata傳入。唯一的例外是,如果參數稍後必須傳入另一個函數,而該函數需要一個指定memory存儲的參數。

GAS -5
確定在被普通用戶調用時會還原的函數可以標記為payable
如果使用了像onlyOwner這樣的函數修飾符,當普通用戶嘗試支付該函數時,該函數將會還原。將函數標記為payable將降低合法調用者的gas成本,因為編譯器不會包括檢查是否提供了支付的檢查。

b.非關鍵問題
以下是關於「非關鍵問題」的繁體中文(ZH-TW)翻譯:


非關鍵問題

編號 問題 實例數
NC-1 require() / revert() 語句應該有描述性的原因字符串 18
NC-2 事件缺少索引字段 5
NC-3 函數如果不在內部使用可以標記為external 1

c. 「中等問題」


中等問題

編號 問題 實例數
M-1 受信任的擁有者的集中化風險 5

[M-1] 受信任的擁有者的集中化風險
影響:
合約有擁有特權權利執行管理任務的擁有者,且需要被信任,以確保不執行惡意更新或撤走資金。


2. 20230201-slither

問題 實例數 性質
solc版本 4個結果 資訊性
命名慣例 18個結果 資訊性
未初始化的本地變量 20個結果 中等
本地變量遮蔽 2個結果
死碼 25個結果 資訊性
未使用的狀態 2個結果 資訊性

3. 20230208- pashov

協議概述

Hypercerts 是一個協議,允許在區塊鏈上高效地建立影響力資金系統(IFSs)。用戶可以鑄造類似於工作“證書”的ERC1155半可替代代幣。這些“證書”可以根據規則配置為分散和/或可轉讓。Hypercerts的一個很好的用例是回顧性資金機制,因為一旦您擁有“證書”,您將能夠在未來獲得回顧性資金支付。鑄造的Hypercerts可以分裂(分散)和合併。當您想要將預期的獎勵的一部分給予/出售給另一方時,分散是一個有用的功能。

任何人都有3種方法可以創建一個新的Hypercert(也稱為“聲明”):

  • 調用mintClaim,將代幣的所有units鑄造給呼叫者
  • 調用mintClaimWithFractions,將代幣分成fractions並鑄造給呼叫者
  • 調用createAllowlist,將創建一個ERC1155代幣,需要一個白名單訪問(通過Merkle樹)才能鑄造它
對於功能3,添加了以下方法來鑄造一個新的Hypercert:
  • mintClaimFromAllowlist - 呼叫者可以通過提交一個proof來鑄造到account,該proof授權他鑄造claimID類型代幣的units數量
  • batchMintClaimsFromAllowlists - 與mintClaimFromAllowlist相同,但對於單次交易中的多次鑄造
對於Hypercerts/claims的所有者,存在以下功能:

-splitValue - 將聲明代幣分成幾部分
-mergeValue - 將部分合併成一個聲明代幣
-burnValue - 燃燒聲明代幣

威脅模型

系統參與者
  • 協議所有者 - 可以升級HypercertMinter合約並暫停/恢復它,在協議初始化時設置
  • 聲明鑄造者 - 可以創建一個新的聲明類型,沒有授權控制
  • 已列入白名單的聲明鑄造者 - 可以從已經存在的類型中鑄造代幣,Merkle樹根在類型創建時設置
  • 類型創建者 - 如果策略是FromCreatorOnly,只有他可以轉移代幣
  • 部分所有者 - 可以轉移、燃燒、分裂和合併聲明的一部分

Q: 市場上協議中有什麼有價值的?
A: 聲明及其部分所有權是有價值的,因為它們可能會在未來收到(例如)回顧性資金的獎勵和/或用於治理。
Q: 協議中可能發生的最糟糕的事情是什麼?

  • 用戶偷竊/燃燒聲明,但不擁有並且不是聲明的操作者
  • 通過分裂或合併生成比預期更多的單位
  • 未經授權的升級/暫停合約

有趣/意想不到的設計選擇:

mintClaimFromAllowlist方法檢查msg.sender是否包含在Merkle樹中,但代幣是鑄造到account地址參數的。batchMintClaimsFromAllowlists功能也是如此,其中msg.sender應該在所有的葉子中。

只鑄造1個單位的代幣意味著它在以後的階段不可分割。UI建議在鑄造時使用100個分數 - 也許這應該在智能合約級別上作為最小值強制執行。

發現摘要

ID 標題 嚴重性
[C-01] 使用者可以將一個代幣分割成比tokenID所持有的單位更多的小數 致命
[H-01] 當代幣索引不是最新的時調用splitValue會覆蓋其他索賠的存儲
[M-01] 未使用的函數參數可能導致用戶端產生錯誤的假設
[M-02] 輸入和數據驗證丟失或不完整
[L-01] 註解中有不正確且可能有危險的假設
[L-02] 缺少事件和不正確的事件參數
[L-03] 建議使用兩步模式進行角色轉移
[L-04] 合約的暫停和升級應該由多重簽名或治理賬戶控制
[I-01] 當前代碼中不需要轉移鉤子 資訊性
[I-02] 未使用的導入、本地變量和自定義錯誤 資訊性
[I-03] 將邏輯合併到一個智能合約中,而不是使用繼承 資訊性
[I-04] 拋出不正確的自定義錯誤 資訊性
[I-05] 註解中的拼寫錯誤 資訊性
[I-06] 接口繼承方法缺少override關鍵字 資訊性
[I-07] 位移操作過於複雜 資訊性
[I-08] 代碼中的多餘檢查 資訊性
[I-09] 不完整或錯誤的NatSpec文檔 資訊性
[I-10] 誤導性的變量名稱 資訊性
[I-11] 未使用Solidity安全pragma的最佳實踐 資訊性
[I-12] 代碼庫中的魔法數字 資訊性

特別針對critical C-1及High H-1去看:

[C-01] 使用者可以將一個代幣分割成比tokenID所持有的單位更多的小數

嚴重性
  • 影響:高,因為它破壞了一個重要的協議不變性
  • 可能性:高,因為這類問題很常見且容易被利用
描述

在SemiFungible1155中的 _splitValue 方法不遵循檢查-效果-交互(Checks-Effects-Interactions pattern)模式,並且它從OpenZeppelin的ERC1155實現中調用 _mintBatch,這實際上會進行一個鉤子調用到接收賬戶作為安全檢查。這個調用是不安全的,因為它可以重新進入 _splitValue 方法,並且由於 tokenValues[_tokenID] 還沒有更新,所以它可以再次將代幣分割成更多的小數,然後重複,直到鑄造出大量的代幣。

建議
遵循檢查-效果-交互(Checks-Effects-Interactions pattern)模式。

- _mintBatch(_account, toIDs, amounts, "");
- tokenValues[_tokenID] = valueLeft;
+ tokenValues[_tokenID] = valueLeft;
+ _mintBatch(_account, toIDs, amounts, "");

其他補充

  1. Audit影片
    Yes

Yes

  1. Checks-Effects-Interactions pattern
    目的: 減少對惡意合約的攻擊面,以防止在外部調用後劫持控制流程。

動機: 以太坊虛擬機不支援並行處理。當呼叫外部地址時,例如轉移以太幣到另一個帳戶時,呼叫的合約也同時將控制流程轉移給外部實體。這個外部實體現在負責控制流程,並且可以執行任何內在的程式碼,如果它是另一個合約的話。大部分情況下,這不會引起任何問題,但如果被呼叫的合約有惡意行為,它可能會改變控制流程並以一個意外的狀態返回給初始合約。一種可能的攻擊方式是重新進入攻擊,即惡意合約在第一次呼叫的函數實例完成之前重新進入初始合約。這種攻擊可以用來重複調用只應該執行一次的函數,並且是以太坊歷史上最著名的黑客攻擊之一:DAO漏洞利用。這種描述的漏洞在其他軟體環境中並不存在,這使得對於不熟悉智能合約開發的開發人員來說很難避免。 本節中介紹的模式,連同安全以太轉帳模式,旨在提供一個安全的解決方案,以使功能免受任何形式的重入攻擊。

應用場景
當有以下狀況時,可使用「檢查效果互動」模式時,

  • it cannot be avoided to hand over control flow to an external entity.
    無法避免將控制流程交給外部實體。
  • you want to guard your functions against re-entrancy attacks.
    你想要保護你的函數免受重入攻擊。

應用 (以一個簡單的銀行合約,用戶可以存款和提取以太幣為例子)

// This code has not been professionally audited, therefore I cannot make any promises about
// safety or correctness. Use at own risk.
contract ChecksEffectsInteractions {

    mapping(address => uint) balances;

    function deposit() public payable {
        balances[msg.sender] = msg.value;
    }

    function withdraw(uint amount) public {
        require(balances[msg.sender] >= amount);

        balances[msg.sender] -= amount;

        msg.sender.transfer(amount);
    }
}

第一步是進行所有必要的檢查。由於這只是一個小例子,只需要檢查一個條件:用戶的餘額是否足夠支付所需金額,這在第10行的 require 語句中完成。下一步是應用所有效果,其中我們再次只有一個效果:在第12行調整用戶的餘額。所有**外部交互都在最後一步進行。**我們使用 transfer() 方法進一步保護該函數免受重入攻擊,詳細說明請參閱安全以太幣轉移模式

In an unsafe implementation of this contract, one disregarding the Checks Effects Interactions pattern, where the order of the effect and interaction in line 12 and 14 are exchanged and not transfer() but the unsafe and low level call.value() is used, a malicious contract could reenter our function. Because the control flow would pass over to the malicious contract, it would be able to call the withdraw() function again, before the first invocation is finished, without being intercepted by our check. This is because the line of code carrying the effect of adjusting the balance would not have been reached yet. Therefore, a second transfer of ether to the attacker would be issued. This circle would keep on draining the contract of ether, until either the transaction runs out of gas or the contracts funds are not sufficient anymore.
在這個不安全的合約實現中,忽略了檢查效果交互模式,其中第12行和第14行的效果和交互順序被交換,並且使用了不安全和底層的 call.value() 。一個惡意合約可以重新進入我們的函數。因為控制流會轉移到惡意合約,它可以在第一次調用完成之前再次調用 withdraw() 函數,而不會被我們的檢查攔截。這是因為調整餘額的代碼行尚未執行到。因此,將發出對攻擊者的第二次以太轉賬。這個循環將持續耗盡合約的以太,直到交易耗盡燃料或合約資金不足。

影響

多数情况下,只需考虑功能代码顺序,而无需更改任何逻辑,就可以应用该模式。在进行外部调用的任何函数中使用这种模式是一个好习惯,无论对方是否值得信任,因为即使是可信任的外部方也可能将控制权转移给第三方,而这可能是恶意的。

其他資料參考介紹 來源

hecks-Effects-Interaction - 保证状态完整,再做外部调用
该模式是编码风格约束,可有效避免重放攻击。通常情况下,一个函数可能包含三个部分:

Checks:参数验证
Effects:修改合约状态
Interaction:外部交互
这个模式要求合约按照Checks-Effects-Interaction的顺序来组织代码。它的好处在于进行外部调用之前,Checks-Effects已完成合约自身状态所有相关工作,使得状态完整、逻辑自洽,这样外部调用就无法利用不完整的状态进行攻击了。


上一篇
Day 24 - testing練習
下一篇
Day 26 - RetroPFG / PCO
系列文
Web3 X 公共財30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言