Các cuộc tấn công từ chối dịch vụ trong hợp đồng thông minh Rust
Một cuộc tấn công từ chối (DoS) dịch vụ có thể khiến hợp đồng thông minh không thể sử dụng được trong một khoảng thời gian. Có một số lý do cho điều này:
Có một khiếm khuyết trong logic hợp đồng là độ phức tạp tính toán quá cao, dẫn đến mức tiêu thụ gas vượt quá giới hạn.
Khi gọi hợp đồng chéo, việc thực thi hợp đồng phụ thuộc vào trạng thái hợp đồng bên ngoài không đáng tin cậy, gây ra tình trạng tắc nghẽn.
Chủ sở hữu hợp đồng mất khóa riêng tư, dẫn đến không thể thực hiện các chức năng đặc quyền chính.
Sau đây là phân tích các lỗ hổng DoS này với các ví dụ cụ thể.
1. Duyệt qua các cấu trúc dữ liệu lớn có thể thay đổi bên ngoài
Dưới đây là một hợp đồng "chia cổ tức" đơn giản, có rủi ro tấn công từ chối dịch vụ:
rỉ sét
#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize)]
pub struct Hợp đồng {
pub registered: Vec\u003caccountid\u003e,
pub accounts: UnorderedMap<accountid, balance="">,
}
pub fn distribute_token(&mut self, amount: u128) {
assert_eq!(env::p redecessor_account_id(), NHÀ PHÂN PHỐI, "ERR_NOT_ALLOWED");
cho cur_account trong self.registered.iter() {
let balance = self.accounts.get(&cur_account).expect("ERR_GET");
self.accounts.insert(&cur_account, &balance.checked_add(amount).expect("ERR_ADD" ));
log!("Cố gắng phân phối đến tài khoản {}", &cur_account);
ext_ft_token::ft_transfer(
cur_account.clone(),
số tiền,
&FTTOKEN,
0,
GAS_FOR_SINGLE_CALL
);
}
}
Ở đây self.registered có thể được mở rộng vô hạn, dẫn đến việc duyệt khi thiếu Gas.
Nên chuyển sang chế độ "rút tiền", để người dùng chủ động rút thưởng:
rỉ sét
pub fn withdraw(&mut self) {
let account_id = env::predecessor_account_id();
let amount = self.accounts.get(&account_id).expect("Không có phần thưởng");
self.accounts.insert(&account_id, &0);
ext_ft_token::ft_transfer(
account_id,
lượng
&FTTOKEN,
0,
GAS_FOR_SINGLE_CALL
);
}
2. Phụ thuộc trạng thái hợp đồng chéo dẫn đến chặn
Đây là hợp đồng "Đấu giá":
rỉ sét
#[near_bindgen]
#[derive(BorshDeserialize, BorshSerialize)]
pub struct Hợp đồng {
quán rượu đã đăng ký: Vec,
pub bid_price: UnorderedMap\u003caccountid, balance=""\u003e,
pub current_leader: AccountId,
pub highest_bid: u128,
pub refund: bool
}
Nếu tài khoản của người đặt giá trước đó bị xóa, giá thầu mới sẽ bị chặn.
Xem xét sự thất bại của các cuộc gọi bên ngoài, các token không thể trả lại có thể được dàn dựng để rút tiếp theo:
gỉ
pub fn withdraw_lost_funds(&mut self) {
let account_id = env::p redecessor_account_id();
let amount = self.lost_funds.get(&account_id).expect("Không có quỹ bị mất");
self.lost_funds.remove(&account_id);
ext_ft_token::ft_transfer(
account_id,
lượng
&FTTOKEN,
0,
GAS_FOR_SINGLE_CALL
);
}
3. Mất khóa riêng của chủ sở hữu
Một số chức năng chính chỉ có thể được gọi bởi chủ sở hữu hợp đồng. Nếu chủ sở hữu mất khóa riêng tư, các chức năng này sẽ không được thực thi.
Nên áp dụng giải pháp ký nhiều để quản lý hợp đồng, tránh điểm lỗi đơn.
pub fn submit_transaction(&mut self, giao dịch: Transaction) {
assert!(self.owners.contains(&env::p redecessor_account_ id()));
Thêm giao dịch vào danh sách đang chờ xử lý
}
pub fn xác nhận_giao_dịch(&mut self, transaction_id: u64) {
assert!(self.owners.contains(&env::p redecessor_account_ id()));
Tăng số lần xác nhận
Nếu đạt đến số lượng xác nhận cần thiết, giao dịch sẽ được thực hiện
}
Thông qua các phương pháp trên, nguy cơ tấn công DoS trong hợp đồng thông minh có thể được ngăn chặn một cách hiệu quả.
</accountid,></accountid,>
Xem bản gốc
Trang này có thể chứa nội dung của bên thứ ba, được cung cấp chỉ nhằm mục đích thông tin (không phải là tuyên bố/bảo đảm) và không được coi là sự chứng thực cho quan điểm của Gate hoặc là lời khuyên về tài chính hoặc chuyên môn. Xem Tuyên bố từ chối trách nhiệm để biết chi tiết.
Hướng dẫn thực chiến phòng chống tấn công DoS trong hợp đồng thông minh Rust
Các cuộc tấn công từ chối dịch vụ trong hợp đồng thông minh Rust
Một cuộc tấn công từ chối (DoS) dịch vụ có thể khiến hợp đồng thông minh không thể sử dụng được trong một khoảng thời gian. Có một số lý do cho điều này:
Có một khiếm khuyết trong logic hợp đồng là độ phức tạp tính toán quá cao, dẫn đến mức tiêu thụ gas vượt quá giới hạn.
Khi gọi hợp đồng chéo, việc thực thi hợp đồng phụ thuộc vào trạng thái hợp đồng bên ngoài không đáng tin cậy, gây ra tình trạng tắc nghẽn.
Chủ sở hữu hợp đồng mất khóa riêng tư, dẫn đến không thể thực hiện các chức năng đặc quyền chính.
Sau đây là phân tích các lỗ hổng DoS này với các ví dụ cụ thể.
1. Duyệt qua các cấu trúc dữ liệu lớn có thể thay đổi bên ngoài
Dưới đây là một hợp đồng "chia cổ tức" đơn giản, có rủi ro tấn công từ chối dịch vụ:
rỉ sét #[near_bindgen] #[derive(BorshDeserialize, BorshSerialize)] pub struct Hợp đồng { pub registered: Vec\u003caccountid\u003e, pub accounts: UnorderedMap<accountid, balance="">, }
pub fn register_account(&mut self) { nếu self.accounts.insert(&env::predecessor_account_id(), &0).is_some() { env::p anic("Tài khoản đã được đăng ký".to_string().as_ bytes()); } else { self.registered.push(env::p redecessor_account_id()); } log!("Tài khoản đã đăng ký {}", env::predecessor_account_id()); }
pub fn distribute_token(&mut self, amount: u128) { assert_eq!(env::p redecessor_account_id(), NHÀ PHÂN PHỐI, "ERR_NOT_ALLOWED");
}
Ở đây self.registered có thể được mở rộng vô hạn, dẫn đến việc duyệt khi thiếu Gas.
Nên chuyển sang chế độ "rút tiền", để người dùng chủ động rút thưởng:
rỉ sét pub fn withdraw(&mut self) { let account_id = env::predecessor_account_id(); let amount = self.accounts.get(&account_id).expect("Không có phần thưởng");
}
2. Phụ thuộc trạng thái hợp đồng chéo dẫn đến chặn
Đây là hợp đồng "Đấu giá":
rỉ sét #[near_bindgen] #[derive(BorshDeserialize, BorshSerialize)] pub struct Hợp đồng { quán rượu đã đăng ký: Vec, pub bid_price: UnorderedMap\u003caccountid, balance=""\u003e, pub current_leader: AccountId, pub highest_bid: u128, pub refund: bool }
pub fn bid(&mut self, sender_id: AccountId, amount: u128) -> PromiseOrValue { assert!(amount > self.highest_bid);
}
#[private] pub fn account_resolve(&mut self, sender_id: AccountId, amount: u128) { match env::promise_result(0) { PromiseResult::NotReady => unreachable!(), 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 = amount; } PromiseResult::Failed => { ext_ft_token::ft_transfer( sender_id.clone(), số lượng, &FTTOKEN, 0, GAS_FOR_SINGLE_CALL * 2, ); log!("quay lại ngay bây giờ"); } }; }
Nếu tài khoản của người đặt giá trước đó bị xóa, giá thầu mới sẽ bị chặn.
Xem xét sự thất bại của các cuộc gọi bên ngoài, các token không thể trả lại có thể được dàn dựng để rút tiếp theo:
gỉ pub fn withdraw_lost_funds(&mut self) { let account_id = env::p redecessor_account_id(); let amount = self.lost_funds.get(&account_id).expect("Không có quỹ bị mất");
}
3. Mất khóa riêng của chủ sở hữu
Một số chức năng chính chỉ có thể được gọi bởi chủ sở hữu hợp đồng. Nếu chủ sở hữu mất khóa riêng tư, các chức năng này sẽ không được thực thi.
Nên áp dụng giải pháp ký nhiều để quản lý hợp đồng, tránh điểm lỗi đơn.
gỉ pub struct MultiSigContract { chủ sở hữu: Vec\u003caccountid\u003e, required_confirmations: u32, }
pub fn submit_transaction(&mut self, giao dịch: Transaction) { assert!(self.owners.contains(&env::p redecessor_account_ id())); Thêm giao dịch vào danh sách đang chờ xử lý }
pub fn xác nhận_giao_dịch(&mut self, transaction_id: u64) { assert!(self.owners.contains(&env::p redecessor_account_ id())); Tăng số lần xác nhận Nếu đạt đến số lượng xác nhận cần thiết, giao dịch sẽ được thực hiện }
Thông qua các phương pháp trên, nguy cơ tấn công DoS trong hợp đồng thông minh có thể được ngăn chặn một cách hiệu quả.