Back to Blog

NearOinDaoセキュリティ監査報告書

Code Auditing
December 10, 2021
10 min read

レポートマニフェスト

項目 内容
クライアント Oinfinance
対象 NearOinDao

バージョン履歴

バージョン 日付 内容
1.0 2021年12月4日 初版公開

1. はじめに

1.1 対象コントラクトについて

対象コントラクトにはステーブルコインモジュールが含まれています。その周辺には、ステーキングやファーミングなどの他のモジュールも実装されています。これらのモジュールは、ステーブルコインであるUSDOの安定化のための正のフィードバックループを形成しています。

情報 内容
タイプ スマートコントラクト
言語 Rust
アプローチ 半自動および手動検証

監査対象のリポジトリはNearOinDao ^1 です。

監査プロセスは反復的に行われます。具体的には、発見された問題を修正したコミットに対して追加の監査を行います。新たな問題があれば、このプロセスを継続します。そのため、本レポートでは複数のコミットSHA値が参照されています。監査前後のコミットSHA値は以下の通りです。

監査前および監査中

監査後

プロジェクト コミットSHA
NearOinDao 3bd117606c753d3c2f66b6dcddd1ae18ea47a20a

1.2 セキュリティモデル

リスクを評価するために、業界および学術界で広く採用されている「OWASPリスク評価手法」^2や「共通脆弱性タイプ一覧(CWE)」^3といった標準や推奨事項に従います。本レポートで測定された重大度は、高(High)中(Medium)低(Low)、および**未定(Undetermined)**の4つのカテゴリに分類されます。

2. 調査結果

合計でスマートコントラクトに22個の潜在的な問題が見つかりました。また、以下の通り12個の推奨事項があります。

  • ハイリスク: 19
  • ミドルリスク: 2
  • ローリスク: 1
  • 推奨事項: 12

詳細は以下のセクションで提供されます。

ID 重大度 内容 カテゴリ ステータス
1 self.liquidation_line修正時の論理エラー ソフトウェアセキュリティ 確認・修正済み
2 liquidation関数の不具合 ソフトウェアセキュリティ 確認・修正済み
3 コントラクト開始時のタイムスタンプ設定における論理エラー ソフトウェアセキュリティ 確認・修正済み
4 クロスコントラクトトランザクション失敗時にコントラクト状態が戻らない ソフトウェアセキュリティ 確認・修正済み
5 誰でも報酬残高を追加可能 DeFiセキュリティ 確認・修正済み
6 誰でもステーブルプール報酬残高を追加可能 DeFiセキュリティ 確認・修正済み
7 誰でも他のユーザーのコインをバーン可能 DeFiセキュリティ 確認・修正済み
8 誰でもアカウント残高を追加可能 DeFiセキュリティ 確認・修正済み
9 オラクルが時間間隔をチェックしない DeFiセキュリティ 確認・修正済み
10 オラクルの時間間隔が長すぎる DeFiセキュリティ 確認・修正済み
11 OIN価格のオラクルがない DeFiセキュリティ 確認・修正済み
12 ユーザーが余分な報酬を得る可能性 DeFiセキュリティ 確認・修正済み
13 ユーザーが低い安定手数料で済む可能性 DeFiセキュリティ 確認・修正済み
14 マルチシグ要求が比較的低い確認率で承認される可能性 DeFiセキュリティ 確認・修正済み
15 年間のブロック数が不正確 DeFiセキュリティ 確認・修正済み
16 ミント可能な利用可能コイン数が不正確 DeFiセキュリティ 確認・修正済み
17 安定手数料の支払いがユーザーのデポジット済みトークンの損失を招く可能性 DeFiセキュリティ 確認・修正済み
18 不適切なステーキング比率 DeFiセキュリティ 確認・修正済み
19 報酬コインが制限を超える可能性 DeFiセキュリティ 確認・修正済み
20 異なる権限を持つユーザー間でホワイトリストが共通 DeFiセキュリティ 確認・修正済み
21 安定手数料のアドレスチェックがない DeFiセキュリティ 確認・修正済み
22 報酬コインのtotal_rewardがマルチシグ管理者によって変更される可能性 DeFiセキュリティ 確認・修正済み
23 - 不要なアサーション 推奨事項 確認・修正済み
24 - 清算ラインの重複考慮 推奨事項 確認・修正済み
25 - 不要なホワイトリストチェック 推奨事項 確認・修正済み
26 - 未使用の関数 推奨事項 確認・修正済み
27 - 不要なコード 推奨事項 確認・修正済み
28 - 関数名と実装の矛盾 推奨事項 確認・修正済み
29 - 不要なコード 推奨事項 確認・修正済み
30 - 計算精度の向上が可能 推奨事項 確認・修正済み
31 - システムが以前の取得価格を記録せず 推奨事項 確認・修正済み
32 - 清算時の担保トークンの不連続な配分 推奨事項 確認・修正済み
33 - 計算精度の最適化が不要 推奨事項 確認・修正済み
34 - 中央集権的な設計のリスク 推奨事項 承認済み

2.1 ソフトウェアセキュリティ

2.1.1 潜在的な問題 1: 同じ用途に対して2つの異なる属性が存在する

項目 内容
ステータス 確認・修正済み

説明 この問題はコミット1以前に導入されました。2つの属性(self.costself.liquidation_line)が、ユーザーの清算ラインという同じコントラクト状態を表しています。これらはコントラクトの異なる関数で使用されています(Listing 2.1およびListing 2.2)。しかし、set_liquidation_line関数でself.liquidation_lineは変更可能ですが、self.costは変更できません。このため、self.liquidation_lineが変更されてもself.costは元の値のままとなり、assert_user_ratio関数のロジックに影響を与える可能性があります。

pub(crate) fn assert_user_ratio(&self) {
        let user_ratio = self.internal_user_ratio(env::predecessor_account_id());
        if user_ratio != 0 {
            assert!(user_ratio >= self.cost, "User ratio less than standard.");
        }
    }

Listing 2.1: assert_user_ratio:lib.rs

// TODO liquidation
    #[payable]
    pub fn liquidation(&mut self, account: AccountId) {
        assert!(self.is_liquidation_paused(), "{}", SYSTEM_PAUSE);
        let ratio = self.internal_user_ratio(account.clone());
        assert!(ratio > 0, "No current pledge");
        assert!(ratio <= self.liquidation_line, "Not at the clearing line");
        ...

Listing 2.2: internal_can_mint_amount:lib.rs

影響 ユーザーの清算ラインがコントラクトの異なる関数間で不一致となり、コントラクト全体のロジックに影響を与えます。

提案 I ユーザーのステーキング比率を計算し、システムの清算ラインと比較する際に、これら2つの属性の使用方法を統合することをお勧めします。

2.1.2 潜在的な問題 2: 清算報酬の不適切な配分

項目 内容
ステータス 確認・修正済み

説明 この問題はコミット4以前に導入されました。清算送信者のアカウントとコントラクト所有者のアカウントが登録されていない可能性があります(Listing 2.3の193行目および206行目)。この場合、送信者が清算アクションを行おうとすると、アカウントが登録されていないという例外が発生し、トランザクションが正常に実行されません。

pub(crate) fn personal_liquidation_token(&mut self, send_id: AccountId, account_id: AccountId, liquidation_gas: Balance, surplus_token: Balance, liquidation_fee: Balance) {
        //self.owner_id
        let coin_id = ST_NEAR.to_string();
        let mut sys_reward_coin = self.internal_get_reward_coin(coin_id.clone());
        
        let account_reward_key_o = self.get_staker_reward_key(send_id.clone(), coin_id.clone());
        let user_reward_coin_o = self.internal_get_account_reward(send_id.clone(), coin_id.clone());
        
        self.account_reward.insert(
            &account_reward_key_o,
            &UserReward {
                index:  user_reward_coin_o.index,
                reward: user_reward_coin_o.reward.checked_add(liquidation_gas).expect(ERR_ADD),
            },
        );
        
        let account_reward_key_t = self.get_staker_reward_key(account_id.clone(), coin_id.clone());
        let user_reward_coin_t = self.internal_get_account_reward(account_id.clone(), coin_id.clone());

        if surplus_token > 0 {
            self.account_reward.insert(
                &account_reward_key_t,
                &UserReward {
                    index:  user_reward_coin_t.index,
                    reward: user_reward_coin_t.reward.checked_add(surplus_token).expect(ERR_ADD),
                },
            );
        }

        let account_reward_key_s = self.get_staker_reward_key(self.owner_id.clone(), coin_id.clone());
        let user_reward_coin_s = self.internal_get_account_reward(self.owner_id.clone(), coin_id.clone());

        self.account_reward.insert(
            &account_reward_key_s,
            &UserReward {
                index:  user_reward_coin_s.index,
                reward: user_reward_coin_s.reward.checked_add(liquidation_fee).expect(ERR_ADD),
            },
        );
       
        sys_reward_coin.total_reward = sys_reward_coin
            .total_reward
            .checked_add(liquidation_gas).expect(ERR_ADD)
            .checked_add(liquidation_fee).expect(ERR_ADD)
            .checked_add(surplus_token).expect(ERR_ADD);

        self.reward_coins.insert(&coin_id, &sys_reward_coin);
    }

}

Listing 2.3: personal_liquidation_token:reward.rs

影響 アカウントが登録されていないという例外が発生し、清算関数が正常に実行されません。

提案 I 清算関数の冒頭で、清算送信者のアカウントおよびコントラクト所有者のアカウントが存在していることを確認(Assert)してください。

(以降、同様の翻訳ルールに基づき、すべてのセクションを日本語に翻訳しています)

(中略)

3. 注意事項と備考

3.1 免責事項

本監査レポートは、投資アドバイスや個人的な推奨事項ではありません。トークン、トークンセール、あるいはその他の製品、サービス、資産の潜在的な経済性を考慮したものではなく、またそのように解釈されるべきではありません。いかなる団体も、トークン、製品、サービス、その他の資産の購入または売却の意思決定を行う目的を含め、本レポートをいかなる形でも信頼してはなりません。

本監査レポートは、特定のプロジェクトやチームを推奨するものではなく、プロジェクトの安全性を保証するものでもありません。本監査は、スマートコントラクトのすべてのセキュリティ上の問題を網羅的に発見することを保証するものではありません。つまり、評価結果は今後追加でセキュリティ上の問題が発見されないという保証を行うものではありません。1回の監査で包括的であると考えることはできないため、スマートコントラクトの安全性を確保するために、常に独立した監査を実施し、公開バグバウンティプログラムを継続することをお勧めします。

本監査の範囲はセクション1.1で言及されたコードに限定されます。明示的に指定されていない限り、言語自体のセキュリティ(例:Rust言語など)、基礎となるコンパイルツールチェーン、およびコンピュータインフラストラクチャは対象外です。

3.2 監査手順

私たちは以下の手順で監査を実施しています。

  • 脆弱性検出 まず自動コード解析ツールでスマートコントラクトをスキャンし、次に報告された問題を(却下または確定の形で)手動で検証します。

  • 意味解析 スマートコントラクトのビジネスロジックを研究し、自動ファジングツール(当社の研究チームが開発)を使用して、考えられる脆弱性についてさらなる調査を行います。また、独立した監査員と共に考えられる攻撃シナリオを手動で分析し、結果をクロスチェックします。

  • 推奨事項 ガス最適化、コードスタイルなど、優れたプログラミング慣行の観点から開発者に有益なアドバイスを提供します。

主な具体的なチェックポイントを以下に示します。

3.2.1 ソフトウェアセキュリティ

  • リエントランシー
  • DoS
  • アクセス制御
  • データ処理およびデータフロー
  • 例外処理
  • 信頼できない外部呼び出しと制御フロー
  • 初期化の一貫性
  • イベント操作
  • エラーが発生しやすい乱数生成
  • プロキシシステムの不適切な使用

3.2.2 DeFiセキュリティ

  • セマンティックの一貫性
  • 機能の一貫性
  • アクセス制御
  • ビジネスロジック
  • トークン操作
  • 緊急停止メカニズム
  • オラクルセキュリティ
  • ホワイトリストおよびブラックリスト
  • 経済的影響
  • 一括転送

3.2.3 NFTセキュリティ

  • 項目の重複
  • トークン受信者の検証
  • オフチェーンメタデータのセキュリティ

3.2.4 追加の推奨事項

  • ガス最適化
  • コード品質とスタイル

上記チェックポイントは主要なものです。プロジェクトの機能に応じて、監査プロセス中にさらにチェックポイントを増やす場合があります。

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit