
この記事では、Rust スマートコントラクトにおける権限制御に関連する問題を、次の 2 つの観点から具体的に紹介します。
コントラクト メソッド (関数) へのアクセス/呼び出しの可視性。
副題
1. コントラクト機能(メソッド)の可視化
スマート コントラクトを作成する場合、コントラクト関数の可視性を指定することで、誰がどの関数を呼び出せるかを制御できます。これにより、契約の特定の重要な部分を偶発的なアクセスや操作から簡単に保護できます。
コントラクト機能の可視性を正しく設定することの重要性を反映するために、この記事では Bancor Network Exchange を例に挙げます。 2020 年 6 月 18 日の時点で、契約の主要な機能に対するアクセス制御権限の誤った設定が原因で、契約資産セキュリティ インシデントが取引所で発生しました。契約はSolidity言語で書かれています。この言語では、コントラクト機能の可視性はパブリック/外部とプライベート/内部に大別されます。前者では、コントラクト関数を外部呼び出し元から呼び出すことができ、コントラクト インターフェイスの一部とみなすことができます。
しかし、Bancor Network Exchange が特定のセキュリティ ホールを修正した際、不注意により、コントラクト内の一部の主要な転送関数を誤ってパブリック属性として設定してしまいました (以下を参照)。
これに基づいて、一般ユーザーを含む誰もが契約外からこれらの関数を呼び出して、自分自身または他人のために対応する転送操作を実行できます。
この重大な脆弱性の存在により、ユーザーの資産 59 万ドルが深刻なリスクにさらされます。
同様に、Rust スマート コントラクトでは、コントラクト関数の可視性制御にも注意を払う必要があります。
この一連のスマート コントラクトでは、日記を開発しますRustスマートコントラクト開発日記(1)、NEAR SDK で定義されたマクロを導入しました: #[near_bindgen]:
#[near_bindgen] は、near-sdk-macros-version パッケージのnear_bindgen 関数によって定義されます。これは、マクロが自動的に挿入コード (Macros-Auto-Generated Injected Code、略して MAGIC) を生成するために使用される場所です。
NEAR が提供する公式の説明ドキュメントを参照すると、#[near_bindgen] マクロで定義された Rust スマート コントラクト関数にいくつかの異なる表示属性があることがわかります。
pub fn: コントラクト メソッドがパブリックであり、コントラクト インターフェイスの一部であることを示します。つまり、誰でもコントラクトの外部から呼び出すことができます。
fn: コントラクトのメソッド関数で pub が明示的に指定されていない場合、その関数はコントラクトの外部から直接呼び出すことができず、コントラクト内の他の関数によって内部的にのみ呼び出すことができることを意味します。
pub(crate) fn: pub(in crate) と同等で、fn と同様に、この可視性修飾子は、クレートのスコープ内で呼び出される特定のコントラクト メソッドを制限できます。
コントラクトのメソッドを内部として設定するもう 1 つの方法は、コントラクト内に別の impl Contract コード ブロックを定義することです。
ただし、実装は #[near_bindgen] によって変更されないことに注意してください。
コールバック (Callbacks) 関数のアクセス制御:
コントラクト内のコールバック関数の定義は、関数呼び出しを通じて呼び出せるように、パブリック属性として設定する必要があります。
コントラクトでコールバック関数を定義するときは、そのコールバック関数が他のユーザーによって自由に呼び出されないようにする必要もあります。つまり、コールバック関数 env::current_account_id() の呼び出し元は、コントラクト自身の env::current_account_id() である必要があります。
NEAR SDK は、同等の Rust マクロ #[private] を定義しています。このマクロを使用すると、コントラクトのコールバック関数は、上記のコードの 4 ~ 5 行目で実装されたものと同じ機能を実現できます。
デフォルトでは、Rust 言語のすべてはプライベートです。たとえば、上記の public 属性を設定しない関数 fn のデフォルトの可視性は private です。
ここで Solidity が区別する必要があるのは、一部の古いバージョンの Solidty コンパイラでは、コントラクト関数の定義に修飾子が追加されていない場合、デフォルトでパブリックとみなされます。
ただし、Rust 言語には 2 つの例外があります。
pub Trait のサブ項目はデフォルトでパブリックです。
pub Enum の Enum 変数もデフォルトではパブリックです。
2. 特権機能のアクセス制御(ホワイトリスト機構)
Rust スマート コントラクトを作成するときは、特定の機能の可視性を知ることに加えて、コントラクトのセマンティック レベルから深く考える必要があります。つまり、完全なアクセス制御ホワイトリスト メカニズムを確立する必要があります。
Solidity スマート コントラクト ライブラリ openzeppelin-contracts で定義および使用されているcontracts/access/Ownable.sol コントラクトと同様に、コントラクトの初期化、コントラクトの開始/一時停止、統合転送などの一部の関数は特権関数です。これらの関数は、通常、所有者のみが呼び出す関数とも呼ばれます。
ただし、所有者は本質的にコントラクトの外部呼び出し元であるため、呼び出すには、これらの主要な関数をパブリック プロパティとして設定する必要があります。では、これらの関数はパブリック属性なので、他のすべての一般ユーザーも呼び出すことができるということでしょうか?
答えは「はい」ですが、所有者ではない一般ユーザーは、調整はできるものの、完全には調整できないことにすぐに気づくでしょう。
これは、スマート コントラクトでは、コントラクト機能に対していくつかのアクセス制御ルールを定義でき、実行を完全に許可するには対応するルールを満たす必要があるためです。たとえば、Solidity コントラクトでは次のような一般的に使用される修飾子があります。
この修飾子によって変更されたコントラクト関数が呼び出されると、このトランザクションの呼び出し元 msg.sender がコントラクトの初期化時に設定された所有者であるかどうかが最初にチェックされ、一致しない場合は、その後の関数の実行が中止または元に戻されます。 , これにより、権限のないユーザーによるアクセスや実行が防止されます。
同様に、NEAR Rust のスマート コントラクトでは、次のようなカスタム Trait を実装することもできます。
この特性を使用すると、コントラクト内の特定の特権関数へのアクセス制御を実装することもできます。つまり、このトランザクション内のコントラクトの呼び出し元 env::predecessor_account_id() はコントラクトの所有者と等しい必要があります。
上記では、所有可能な特権関数のみを対象とした単純なホワイトリストの例を確立しました。この原則に基づいて、より複雑な修飾子や特性をカスタマイズすることによってホワイトリストに複数のユーザーを設定したり、複数のホワイトリストを設定して、優れたきめ細かいグループ アクセス制御効果を実現したりできます。
3. さらなるアクセス制御方法
他の Rust スマート コントラクトのアクセス制御メソッド:
契約通話タイミング制御
コントラクト機能のマルチシグネチャ呼び出しメカニズム、ガバナンスの実装(DAO)
...
スマートコントラクト開発日記シリーズの続報にご注目ください😊