
0. 回顧
1. 概述
在前一篇部落格中,我們討論了如何升級程式。在本文中,我們將介紹與存取控制相關的問題,這是 DeFi 領域中最常見且最基本的安全主題之一。
2. 指令
在 Solana 中,每個程式匯出一個單一的 entrypoint,並以 entrypoint! 定義。與以太坊不同,客戶端只能調用定義為 entrypoint 的單一函數,通常稱為 process_instruction。entrypoint 函數接收三個參數,分別是智能合約的程式 ID、程式將操作的帳戶,以及指令資料。指令資料指定將調用哪條指令。下圖展示了一個範例。透過解包指令資料,可以選擇不同的指令(例如 Lock、Unlock)。因此,從 entrypoint 可以訪問的指令對所有人都是公開的,並可以使用指定的指令資料來執行。

3. 帳戶驗證
如前所述,程式接收需要讀取或寫入的帳戶。這種設計帶來了兩個問題。對於要讀取的帳戶,如何保證帳戶中儲存的資料是可信的。對於要寫入的帳戶,如何保證只有具有特權的使用者才能調用指令來寫入帳戶。以下,我們說明存取控制問題。所有測試程式碼可以在這裡找到。
3.1 程式碼審查(PrivilegeOwner)


我們首先定義兩個結構體,分別是 Door 和 Config。只有在結構體 door 中指定的 key 帳戶(第 17 行)才能開啟所建立的 door。然而,當系統狀態被鎖定時,門無法被開啟,這在結構體 Config 中指定(第 81 行)。

如前所述,Config 帳戶指定門是否可以被開啟。在這種情況下,程式中應只有一個 Config 帳戶。為了實現這一點,我們使用 PDA 來儲存 Config 的資料。在初始化 Config 帳戶後,我們將屬性 is_initialized 設置為 true,使其無法被攻擊者再次初始化(第 108 行至第 110 行)。

指令 Open() 用於開啟門。該指令接收多個帳戶,包括要開啟的門帳戶、config 帳戶,以及旨在開啟 door 的 owner 帳戶。為了保證門屬於該程式且配置有效,我們檢查 door 帳戶和 config 帳戶的擁有者(第 204 行至第 205 行)。這防止了惡意使用者提供假帳戶。這回答了我們的第一個問題。為了保證要讀取的帳戶是可信的,我們需要檢查帳戶的擁有者!請注意,只有 door 帳戶的擁有者才能開啟門。在這種情況下,我們檢查 owner 帳戶是否是 door 的真實 owner,更重要的是,指令是否由該擁有者授權(第 217 行至第 219 行)。

在函數 validate_owner() 中,我們首先檢查這兩個帳戶的公鑰是否相同,然後檢查擁有者的簽名。這回答了第二個問題,為了保證只有具有特權的使用者才能調用 open 指令,我們需要檢查帳戶的擁有者和簽署者。close 指令與 open 類似,詳細資訊可以在程式碼中找到。
我們將程式部署在測試網上,可以在以下連結中找到。
https://explorer.solana.com/address/2Q7FFMWCthBvc6ubLQRx9TRswvaimmd66VaCAfHwsYuC?cluster=testnet
所有測試交易列於下方。此交易的整個過程為 AllocatePDA()-> InitializeDoor()-> InitializeConfig()-> Unlock() -> Open() -> Close()。
https://explorer.solana.com/tx/2X9CyMrHTNEvbzXTE95gem2j8spnvsQsabFeSpV8hiNpYjiQPPzLRqt5KN86ZYRjnQvydvs7y5eUjJK7no8knDhk?cluster=testnet
https://explorer.solana.com/tx/2XfVWiXeQeHbpqAEYm3AH2RU6hunnqtr155EC4EAM5Bq9VVZNP6QocAav9cPjEQdJFcQrbsSSxiKadr4HPMov8pz?cluster=testnet
https://explorer.solana.com/tx/5Em41sg7yFXeNpnEJnhUQJanfLWKwjMqiBeNAqEEzFrSN9P8zKKafcv5F7RKT2pseB171qeoa8Uz4fKgazzayCnW?cluster=testnet
https://explorer.solana.com/tx/2PMtzpSgjnKDLGmRWBdUSFBPimWnudCPekUYbWzPzokENFYa4N4ab4HCtynfGrzswFPTgGYKHU8PccUMHv3mXHkR?cluster=testnet
https://explorer.solana.com/tx/3kviP9MqkWGMV4yA7k7yPQ5BGfXmcYLcctmY1u2D7n56eT1nx8jMtDumkUNJy8yA3KkmzrmfQLjqpigc8ehGZzBN?cluster=testnet
https://explorer.solana.com/tx/38iEaJBzuGMLbfcszdVB8pkniezH8JrA3XGq7JdADZTQ4hNQC82GSTUA2bmcypdVy3t7htWnUzkZ4F8EakmNvqz8?cluster=testnet
3.2 攻擊交易
為了說明擁有者檢查和簽署者檢查的重要性,我們使用兩個攻擊場景作為範例。
第一個場景
第一個場景是 'door' 的擁有者試圖在 config 被鎖定時開啟門。為此,我們在另一個程式中建立一個假的 config 帳戶,並將屬性 is_lock 的值設置為 false。自定義程式的程式碼如下所示。
我們發送交易以建立假的 config 帳戶,假 config 帳戶的公鑰為:2MtSrbWp24VjPZQcSUkiWrvNro7qqKemVCsh3Yxc8LTy。
https://explorer.solana.com/tx/2qSyrL5gdQXmgGCFzmzMm1StFQRkDgWpss9A9jV11q2fgDGM5C1XRuXvbX1N5Dt3q2pRqnmyXHVtXGF5dqadAzpJ?cluster=testnet
一旦假的 config 帳戶建立完成,我們將其輸入程式(第 423 行)。

結果如下所示,日誌顯示 incorrect program id for instruction,這意味著 config 帳戶的擁有者必須是該程式。因此,攻擊者無法繞過此檢查。

第二個場景
第二個場景是惡意使用者試圖在門未鎖定時開啟門。

在這種情況下,我們將真實的 owner 帳戶輸入程式(第 419 行)並發送交易。結果如下所示。

日誌顯示 Signature verification failed,這意味著真實擁有者必須簽署交易才能開啟門,因此我們的第二次攻擊也失敗了。
4. 總結
在 Solana 中,指令根據不同的帳戶實現指定的邏輯,這些帳戶由客戶端或其他程式提供。因此,對帳戶進行適當的檢查相當重要。
在本文中,我們介紹了如何正確地檢查帳戶,並使用兩個攻擊場景說明了這些檢查的重要性。請持續關注,將有更多文章分享。
閱讀本系列的其他文章:
- 保護 Solana 生態系統 (1) — Hello Solana
- 保護 Solana 生態系統 (2) — 程式間調用
- 保護 Solana 生態系統 (3) — 程式升級
- 保護 Solana 生態系統 (5) — 多重簽名
- 保護 Solana 生態系統 (6) — 多重簽名2
- 保護 Solana 生態系統 (7) — 類型混淆
關於 BlockSec
BlockSec 是一家開創性的區塊鏈安全公司,由一群享譽全球的安全專家於 2021 年創立。該公司致力於提升新興 Web3 世界的安全性與可用性,以促進其大規模應用。為此,BlockSec 提供智能合約和 EVM 鏈安全審計服務、用於安全開發和主動封鎖威脅的 Phalcon 平台、用於資金追蹤與調查的 MetaSleuth 平台,以及供 Web3 建構者在加密世界中高效瀏覽的 MetaSuites 擴充功能。
迄今為止,該公司已服務超過 300 位知名客戶,包括 MetaMask、Uniswap Foundation、Compound、Forta 和 PancakeSwap,並在兩輪融資中從卓越的投資者處獲得數千萬美元,這些投資者包括 Matrix Partners、Vitalbridge Capital 和 Fenbushi Capital。
官方 Twitter 帳號:https://twitter.com/BlockSecTeam




