Back to Blog

保護 Solana 生態系統 (4) — 帳戶驗證

April 1, 2022
6 min read

0. 回顧

1. 概述

在上一篇部落格中,我們討論了如何升級程式。在本篇文章中,我們將介紹與存取控制相關的問題,這是 DeFi 領域中最常見且基礎的安全主題之一。

2. 指令

在 Solana 中,每個程式都會導出一個單一的 entrypoint,該入口點是通過 entrypoint! 定義的。與以太坊不同,客戶端只能調用定義為入口點的單一函數,通常稱為 process_instruction。入口點函數接收三個參數:智慧合約的程式 ID、程式將要操作的帳戶,以及指令資料。指令資料指定了將要調用哪個指令。下圖顯示了一個範例。通過解包指令資料,可以選擇不同的指令(例如:Lock、Unlock)。因此,從入口點可以訪問的指令對所有人公開,並且可以使用指定的指令資料執行。

3. 帳戶驗證

如前所述,程式會接收它需要讀取或寫入的帳戶。這種設計帶來了兩個問題:對於要讀取的帳戶,如何保證帳戶中存儲的資料是可信的?對於要寫入的帳戶,如何保證只有特權用戶可以調用指令來寫入這些帳戶?以下我們將說明存取控制問題。所有的測試代碼都可以在這裡找到。

3.1 代碼審查 (PrivilegeOwner)

我們首先定義了兩個結構,分別是 DoorConfig。只有 door 結構中指定的金鑰帳戶(第 17 行)才能打開創建的 door。然而,當系統狀態被鎖定時(由結構 Config 中的第 81 行指定),門無法被打開。

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

Open() 指令用於打開門。該指令接收多個帳戶,包括要打開的門帳戶、config 帳戶,以及旨在打開 doorowner 帳戶。為了確保門屬於該程式且配置有效,我們檢查 door 帳戶和 config 帳戶的擁有者(第 204 行 - 第 205 行)。這防止了惡意用戶輸入虛假帳戶。這回答了我們的第一個問題:為了保證要讀取的帳戶是可信的,我們需要檢查該帳戶的擁有者!請注意,只有 door 帳戶的擁有者可以開門。在此情況下,我們檢查擁有者帳戶是否為 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 帳戶的擁有者必須是該程式。因此,攻擊者無法繞過此檢查。

第二個場景

第二個場景是惡意用戶試圖在門未鎖定時打開門。

在此情況下,我們將真實的擁有者帳戶輸入到程式中(第 419 行)並發送交易。結果顯示如下。

日誌列印出 Signature verification failed,這意味著真正的擁有者必須簽署交易才能打開門,因此我們的第二次攻擊也失敗了。

4. 總結

在 Solana 中,指令根據客戶端或其他程式提供的不同帳戶來執行指定的邏輯。因此,對帳戶進行適當的檢查非常重要。

在本文中,我們介紹了如何正確檢查帳戶,並使用兩個攻擊場景說明了這些檢查的重要性。請持續關注,後續將分享更多文章。

閱讀本系列的其它文章:


關於 BlockSec

BlockSec 是一家開創性的區塊鏈安全公司,由一群全球傑出的安全專家於 2021 年創立。公司致力於提升新興 Web3 世界的安全性與可用性,以促進其大規模採用。為此,BlockSec 提供智慧合約與 EVM 鏈的安全審計服務、用於安全開發與主動阻斷威脅的 Phalcon 平台、用於資金追蹤與調查的 MetaSleuth 平台,以及協助 Web3 構建者在加密世界中高效航行的 MetaSuites 插件。

迄今為止,公司已為超過 300 家知名客戶提供服務,例如 MetaMask、Uniswap Foundation、Compound、Forta 和 PancakeSwap,並從 Matrix Partners、Vitalbridge Capital 和 Fenbushi Capital 等頂級投資機構獲得了兩輪數千萬美元的融資。

官方網站:https://blocksec.com/

官方 Twitter 帳號:https://twitter.com/BlockSecTeam