関連記事:
Rustスマートコントラクト開発日記(3) Rustスマートコントラクトのデプロイ、関数呼び出し、エクスプローラーの使い方
DoS (サービス拒否) 攻撃としても知られるサービス拒否攻撃。このタイプの攻撃により、ユーザーはスマート コントラクトを一定期間 (永久的にも) 正常に使用できなくなります。
DoS (サービス拒否) 攻撃としても知られるサービス拒否攻撃。このタイプの攻撃により、ユーザーはスマート コントラクトを一定期間 (永久的にも) 正常に使用できなくなります。
現在判明している原因は大きく以下の2つに分けられます。
契約ロジックに特定の欠陥があります。パブリック関数など、その実装では計算の複雑さは考慮されません。ユーザーがこの関数を呼び出すと、実際に消費されるガスは、NEAR パブリック チェーンのジェネシス ブロック構成ファイル (genesis_config.json) で定義されたガスを超えます。"max_total_prepaid_gas": 300000000000000` (300TGas)、トランザクションが失敗します。
クロスコントラクトコールの場合、コントラクトの実行は他の外部コントラクトの実行ステータスに依存する場合があります。ただし、外部契約の履行が常に信頼できるとは限らず、本契約の履行が外部契約によって妨げられ、通常どおりに動作しなくなる可能性があります。この種の問題の発生は、契約中の契約ユーザーの資金がロックされ、正常にチャージまたは引き出しができないために現れることがあります。
DoS 現象の原因は、コントラクト ロジックの欠陥に加えて、人的要因にも起因する可能性があります。典型的な例: コントラクトの所有者が秘密キーを紛失したため、コントラクト内のいくつかのonly_owner 実行可能特権関数を実行できなくなります。契約内の一部の重要なシステム ステータス値が時間内に更新されず、プロジェクトに大きな損失が発生する可能性があります。
副題
1. 外部呼び出しによって変更できるデータ構造をループします。
以下は、契約に登録されているユーザーへの「配当」のための単純なスマート コントラクトであり、その状態データは次のとおりです。
ユーザーは、pub fn register_account() 関数を呼び出して登録および初期化できます。
その後、コントラクトの管理者は pub fn distribution_token 関数を呼び出して、システム内のユーザーへの配布を実行します。"配当金"。 「配当」の方法は、ユーザー配列 self.registered を走査し、報酬としてクロスコントラクト呼び出しを通じて指定された量のトークンを各ユーザーに転送することです。
ただし、契約状態データ (self.registered) のサイズには制限がないため、悪意のあるユーザーによって操作され、契約データのサイズが大きくなりすぎる可能性があります。そのため、DISTRIBUTOR ユーザーがこの契約方法を呼び出した場合、消費される可能性のあるガス料金が高くなり、GAS LIMIT を超えます。
以下は実際のNEAR Localnetでの契約のテスト結果です
システムに多くの登録ユーザーがいる場合、distribute_token の実際の実行プロセス中に、prepaid_gas セットではすべてのユーザーの転送操作を満たすのに十分ではないため、このトランザクションは失敗することがわかります。
推奨される解決策:
ガス制限の制限により、コントラクト メソッドの実行中に大規模なデータ構造をトラバースすることはお勧めできません (データ構造のサイズは外部ユーザーによって操作される可能性があります)。本当にトラバースする必要がある場合は、データ構造のサイズを制限し、データ構造のサイズが最大値に達したときにガス制限の制限に触れないようにする必要もあります。
副題
2. クロスコントラクト間の状態依存関係によりコントラクトのブロックが発生する
コントラクトがクロスコントラクト呼び出しを行う場合、外部コントラクトの状態に依存する可能性があります。不適切な依存関係によりコントラクトがブロックされ、DoS 攻撃が発生する可能性があります。
スマート コントラクトが「入札」に使用されるシナリオを考えてみましょう。
ユーザーは、「入札契約」の pub fn register_account 関数メソッドを呼び出してアカウントを登録し、以降の入札に参加する準備をすることができます。
ユーザーは、次のインターフェイス関数を通じて、現在のシステムでこれまでの最高入札額のユーザー ID と入札価格を照会することもできます。
ユーザーは、次のインターフェイス関数を通じて、現在のシステムでこれまでの最高入札額のユーザー ID と入札価格を照会することもできます。
入札コントラクトはトークンを受け取ると、ft_on_transfer 関数を通じて次の入札関数を呼び出します。
この入札機能では、機能の実行ロジックはまず、現在のユーザーの入札が以前の最高入札者の入札値よりも高いかどうかをチェックします。条件が満たされた場合、self.refund_exe() が実行され、「入札契約」から以前の最高入札者の入札トークンが返されます。次に、ユーザー ID をこれまでの最高入札額と入札価格で更新します。
実際の状況は、契約の論理的定義によれば、これまでに最高額の入札を行ったユーザーの ID を置き換えるために、最高額の入札を行ったユーザーの入札トークンを返さなければなりません。
この入札機能では、機能の実行ロジックはまず、現在のユーザーの入札が以前の最高入札者の入札値よりも高いかどうかをチェックします。条件が満たされた場合、self.refund_exe() が実行され、「入札契約」から以前の最高入札者の入札トークンが返されます。次に、ユーザー ID をこれまでの最高入札額と入札価格で更新します。
実際の状況は、契約の論理的定義によれば、これまでに最高額の入札を行ったユーザーの ID を置き換えるために、最高額の入札を行ったユーザーの入札トークンを返さなければなりません。
現時点では、テストでは「入札システム」の参加ユーザーである user0、user1、user2 をシミュレートします。
彼らはそれぞれ 10,000 の初期トークンを持っています。 user0 は最初に「入札システム」で 1000 を入札します。このとき、クエリは current_leader: user0.test.near biggest_bid: 1000 を示します。その後、user0 はすぐに残りの 9000 トークンを user2 に転送し、トークン アカウントを破棄しました。
その後、user1 が 2000 で入札すると、システムは user0 の以前の入札値を返すことを計画します。ただし、この時点では user0 のアカウントは存在しないため、システムはプロンプトを表示します。"Cannot Refund"の場合、後続のトランザクション更新ステータスを正常に完了できません。
この時点で、2 番目の入札者は 2000 で入札したいと考えています。
解決:
副題
3. 所有者の秘密鍵を紛失した
分散型スマート コントラクト プロジェクトでは、部分的な集中化がしばしば存在します。たとえば、契約所有者が存在します。一部のコントラクト関数の実行は所有者のみが実行できるように設定されており、コントラクト内のいくつかの主要なシステム変数の値を設定および変更するために使用されます。このような関数は、only_owner 型関数として呼び出すことができます。
たとえば、上記の「配当」コントラクトで定義された pub fn distribution_token の場合、この関数はonly_owner 関数です。コントラクトの所有者がその機能を実行できない場合(秘密キーを紛失した場合)、資金は常にコントラクト内にロックされ、他のユーザーに配布することはできません。ほとんどの場合、only_owner 関数を使用して、コントラクト内のすべてのトランザクションを一時停止または再開することもできます。これは、所有者がその機能を正常に実行することが重要であることを示しています。
解決:
所有者の上記の個人的な「障害」を回避するために、契約を共同管理する複数の契約所有者を追加したり、元の契約権限制御スキームを置き換えるために複数署名要求の方法を使用したりすることもできます。契約ガバナンス効果の分散化を実現します。スマートコントラクトにおけるマルチシグネチャリクエスト機能の設計と実装については、次回の「スマートコントラクト開発日記」で詳しく説明します。
