# Rustスマートコントラクト安全:防御サービス拒否攻撃サービス拒否攻撃(DoS)攻撃は、スマートコントラクトが一定期間正常に使用できなくなったり、さらには永久に機能不全に陥る原因となります。主な原因には次のようなものがあります:1. 合約ロジックに計算の複雑度が高すぎる欠陥が存在し、Gas消費が制限を超えています。2. クロスコントラクト呼び出し時の外部コントラクトの実行状態への依存は、外部コントラクトによってブロックされる可能性があります。3. コントラクトの所有者が秘密鍵を失い、重要な特権関数を実行できません。以下に具体例を用いて分析します。## 1. 外部から操作可能な大規模データ構造の走査を避ける以下は簡単な"分配"スマートコントラクトで、DoSリスクがあります:さび#[near_bindgen]#[derive(BorshDeserialize、BorshSerialize)]pub struct コントラクト { パブ登録:Vec<accountid>、 パブアカウント: UnorderedMap<accountid, balance="">,}pub fn register_account(&mut self) { self.accounts.insert(&env::p redecessor_account_id()、&0).is_の場合 some() { env::panic("アカウントはすでに登録されています".to_string().as_bytes()); } else { self.registered.push(env::p redecessor_account_id()); } log!("登録済みアカウント {}", env::p redecessor_account_id());}pub fn distribute_token(&mut self, amount: u128) { assert_eq!(env::p redecessor_account_id(), ディストリビューター, "ERR_NOT_ALLOWED"); for cur_account in self.registered.iter() { バランス= self.accounts.get(&cur_account).expect("ERR_GET"); self.accounts.insert(&cur_account, &balance.checked_add(amount).expect("ERR_ADD" )); log!("アカウント {} に配布を試みる", &cur_account); ext_ft_token::ft_transfer( cur_account.clone()、 量 &FTTOKENや 0, GAS_FOR_SINGLE_CALL ); }}この契約の問題は、registered配列のサイズに制限がなく、悪意のあるユーザーによって過大に操作される可能性があるため、distribute_token関数のGas消費が制限を超えることです。推奨される解決策は「出金」モードを採用することです:サビpub fn distribute_token(&mut self, amount: u128) { assert_eq!(env::p redecessor_account_id(), ディストリビューター, "ERR_NOT_ALLOWED"); self.registered.iter() { バランス= self.accounts.get(&account).unwrap(); self.accounts.insert(&account, &(balance + amount)); }}pub fn withdraw(&mut self) { アカウント= env::p redecessor_account_id(); let amount = self.accounts.get(&account).unwrap(); self.accounts.insert(&アカウント、&0); ext_ft_token::ft_transfer(account, 金額, &FTTOKEN, 0, GAS_FOR_SINGLE_CALL);}! [](https://img-cdn.gateio.im/social/moments-b7bbfcf4423b1cf19db56a3af95a7486)## 2. コントラクト間の状態依存を避ける以下は"入札"契約の例です:さび#[near_bindgen]#[derive(BorshDeserialize、BorshSerialize)]pub struct コントラクト { パブ登録:Vec<accountid>、 pub bid_price: UnorderedMap<accountid,balance>, 公開current_leader: AccountId, パブhighest_bid:U128、}pub fn bid(&mut self, sender_id: AccountId, amount: u128) -> PromiseOrValue<u128> { アサート!(amount > self.highest_bid); if self.current_leader == DEFAULT_ACCOUNT { self.current_leader = sender_id; self.highest_bid = 金額; } else { ext_ft_token::account_exist( self.current_leader.clone()、 &FTTOKENや 0, env::p repaid_gas() - GAS_FOR_SINGLE_CALL * 4、 ).then(ext_self::account_resolve( sender_id、 量 &env::current_account_id()、 0, GAS_FOR_SINGLE_CALL*3、 )); } PromiseOrValue::Value(0)}#[private]pub fn account_resolve(&mut self, sender_id: AccountId, amount: u128) { 一致 env::p romise_result(0) { PromiseResult::Successful(_) => { ext_ft_token::ft_transfer( self.current_leader.clone()、 self.highest_bid、 &FTTOKENや 0, GAS_FOR_SINGLE_CALL*2、 ); self.current_leader = sender_id; self.highest_bid = 金額; } PromiseResult::失敗 => { ext_ft_token::ft_transfer( sender_id.clone()、 量 &FTTOKENや 0, GAS_FOR_SINGLE_CALL*2、 ); log!("今すぐ戻る"); } PromiseResult::NotReady => 到達不能!() };}この契約の問題は、前の最高入札者がアカウントをキャンセルした場合、新しい入札者が状態を正常に更新できなくなることです。解決策は合理的なエラーハンドリングを実装することです。例えば、返却できないトークンを一時保管し、後でユーザーが自分で引き出すようにします。さびpub struct コントラクト { // ... pub lost_found: UnorderedMap<accountid, balance="">,}pub fn account_resolve(&mut self, sender_id: AccountId, amount: u128) { 一致 env::p romise_result(0) { PromiseResult::Successful(_) => { // 通常の返金ロジック } PromiseResult::失敗 => { // 返金できないトークンを一時保管 let lost_amount = self.lost_found.get(&self.current_leader).unwrap_or(0); self.lost_found.insert(&self.current_leader, &(lost_amount + self.highest_bid)); // ステータスを更新 self.current_leader = sender_id; self.highest_bid = 金額; } PromiseResult::NotReady => 到達不能!() }}pub fn withdraw_lost_found(&mut self) { アカウント= env::p redecessor_account_id(); let amount = self.lost_found.get(&account).unwrap_or(0); if 金額 > 0 { self.lost_found.insert(&account, &0); ext_ft_token::ft_transfer(account, 金額, &FTTOKEN, 0, GAS_FOR_SINGLE_CALL); }}## 3. 単一障害点の回避コントラクト所有者の秘密鍵が失われると、重要な特権機能が使用できなくなります。解決策はマルチシグネチャメカニズムを採用することです。サビpub struct コントラクト { パブのオーナー:ベック<accountid>、 パブrequired_confirmations:U64、 パブトランザクション:Vec<transaction>、}pub fn propose_transaction(&mut self, transaction: Transaction) { assert!(self.owners.contains(&env::p redecessor_account_id()), "いいえ 所有者"); self.transactions.push(トランザクション);}pub fn confirm_transaction(&mut self, transaction_id: u64) { assert!(self.owners.contains(&env::p redecessor_account_id()), "いいえ 所有者"); let transaction = &mut self.transactions[transaction_id as usize]; transaction.confirmations += 1; if transaction.confirmations >= self.required_confirmations { self.execute_transaction(transaction_id); }}fn execute_transaction(&mut self, transaction_id: u64) { let transaction = self.transactions.remove(transaction_id as usize); // 取引ロジックを実行}マルチシグネチャ機構を通じて、単一の所有者の秘密鍵の喪失による契約の麻痺を回避し、契約の非中央集権性と安全性を向上させることができます。! [](https://img-cdn.gateio.im/social/moments-7076cf1226a2276d1e4cd994d259841f)</transaction></accountid></accountid,></u128></accountid,balance></accountid></accountid,></accountid>
Rustスマートコントラクトの安全性: DoS攻撃を防ぐための3つの重要な戦略
Rustスマートコントラクト安全:防御サービス拒否攻撃
サービス拒否攻撃(DoS)攻撃は、スマートコントラクトが一定期間正常に使用できなくなったり、さらには永久に機能不全に陥る原因となります。主な原因には次のようなものがあります:
合約ロジックに計算の複雑度が高すぎる欠陥が存在し、Gas消費が制限を超えています。
クロスコントラクト呼び出し時の外部コントラクトの実行状態への依存は、外部コントラクトによってブロックされる可能性があります。
コントラクトの所有者が秘密鍵を失い、重要な特権関数を実行できません。
以下に具体例を用いて分析します。
1. 外部から操作可能な大規模データ構造の走査を避ける
以下は簡単な"分配"スマートコントラクトで、DoSリスクがあります:
さび #[near_bindgen] #[derive(BorshDeserialize、BorshSerialize)] pub struct コントラクト { パブ登録:Vec、 パブアカウント: UnorderedMap<accountid, balance="">, }
pub fn register_account(&mut self) { self.accounts.insert(&env::p redecessor_account_id()、&0).is_の場合 some() { env::panic("アカウントはすでに登録されています".to_string().as_bytes()); } else { self.registered.push(env::p redecessor_account_id()); } log!("登録済みアカウント {}", env::p redecessor_account_id()); }
pub fn distribute_token(&mut self, amount: u128) { assert_eq!(env::p redecessor_account_id(), ディストリビューター, "ERR_NOT_ALLOWED");
}
この契約の問題は、registered配列のサイズに制限がなく、悪意のあるユーザーによって過大に操作される可能性があるため、distribute_token関数のGas消費が制限を超えることです。
推奨される解決策は「出金」モードを採用することです:
サビ pub fn distribute_token(&mut self, amount: u128) { assert_eq!(env::p redecessor_account_id(), ディストリビューター, "ERR_NOT_ALLOWED");
}
pub fn withdraw(&mut self) { アカウント= env::p redecessor_account_id(); let amount = self.accounts.get(&account).unwrap(); self.accounts.insert(&アカウント、&0);
}
!
2. コントラクト間の状態依存を避ける
以下は"入札"契約の例です:
さび #[near_bindgen] #[derive(BorshDeserialize、BorshSerialize)] pub struct コントラクト { パブ登録:Vec、 pub bid_price: UnorderedMap<accountid,balance>, 公開current_leader: AccountId, パブhighest_bid:U128、 }
pub fn bid(&mut self, sender_id: AccountId, amount: u128) -> PromiseOrValue { アサート!(amount > self.highest_bid);
}
#[private] pub fn account_resolve(&mut self, sender_id: AccountId, amount: u128) { 一致 env::p romise_result(0) { PromiseResult::Successful(_) => { ext_ft_token::ft_transfer( self.current_leader.clone()、 self.highest_bid、 &FTTOKENや 0, GAS_FOR_SINGLE_CALL2、 ); self.current_leader = sender_id; self.highest_bid = 金額; } PromiseResult::失敗 => { ext_ft_token::ft_transfer( sender_id.clone()、 量 &FTTOKENや 0, GAS_FOR_SINGLE_CALL2、 ); log!("今すぐ戻る"); } PromiseResult::NotReady => 到達不能!() }; }
この契約の問題は、前の最高入札者がアカウントをキャンセルした場合、新しい入札者が状態を正常に更新できなくなることです。
解決策は合理的なエラーハンドリングを実装することです。例えば、返却できないトークンを一時保管し、後でユーザーが自分で引き出すようにします。
さび pub struct コントラクト { // ... pub lost_found: UnorderedMap<accountid, balance="">, }
pub fn account_resolve(&mut self, sender_id: AccountId, amount: u128) { 一致 env::p romise_result(0) { PromiseResult::Successful(_) => { // 通常の返金ロジック } PromiseResult::失敗 => { // 返金できないトークンを一時保管 let lost_amount = self.lost_found.get(&self.current_leader).unwrap_or(0); self.lost_found.insert(&self.current_leader, &(lost_amount + self.highest_bid));
}
pub fn withdraw_lost_found(&mut self) { アカウント= env::p redecessor_account_id(); let amount = self.lost_found.get(&account).unwrap_or(0); if 金額 > 0 { self.lost_found.insert(&account, &0); ext_ft_token::ft_transfer(account, 金額, &FTTOKEN, 0, GAS_FOR_SINGLE_CALL); } }
3. 単一障害点の回避
コントラクト所有者の秘密鍵が失われると、重要な特権機能が使用できなくなります。解決策はマルチシグネチャメカニズムを採用することです。
サビ pub struct コントラクト { パブのオーナー:ベック、 パブrequired_confirmations:U64、 パブトランザクション:Vec、 }
pub fn propose_transaction(&mut self, transaction: Transaction) { assert!(self.owners.contains(&env::p redecessor_account_id()), "いいえ 所有者"); self.transactions.push(トランザクション); }
pub fn confirm_transaction(&mut self, transaction_id: u64) { assert!(self.owners.contains(&env::p redecessor_account_id()), "いいえ 所有者");
}
fn execute_transaction(&mut self, transaction_id: u64) { let transaction = self.transactions.remove(transaction_id as usize); // 取引ロジックを実行 }
マルチシグネチャ機構を通じて、単一の所有者の秘密鍵の喪失による契約の麻痺を回避し、契約の非中央集権性と安全性を向上させることができます。
! </accountid,></accountid,balance></accountid,>