Nhật ký phát triển hợp đồng thông minh Rust (6)
BlockSec
2022-03-29 10:35
本文约2976字,阅读全文需要约12分钟
Tấn công từ chối dịch vụ hay còn gọi là tấn công DoS (Denial of Service), kiểu tấn công này sẽ khiến hợp đồng thông minh không thể được người dùng sử dụng bình thường trong một khoảng thời gian (

Những bài viết liên quan:

Tấn công từ chối dịch vụ hay còn gọi là tấn công DoS (Denial of Service), kiểu tấn công này sẽ khiến hợp đồng thông minh không thể được người dùng sử dụng bình thường trong một khoảng thời gian (thậm chí là vĩnh viễn).

Các nguyên nhân hiện được biết đến có thể được tạm chia thành hai loại sau:

  • Một số sai sót trong logic hợp đồng. Chẳng hạn như một chức năng công khai, việc triển khai nó không tính đến độ phức tạp tính toán. Khi người dùng gọi chức năng này, lượng gas tiêu thụ thực tế sẽ vượt quá lượng gas được xác định trong tệp cấu hình khối genesis chuỗi công khai NEAR (genesis_config.json)"max_total_prepaid_gas": 300000000000000` (300TGas), khiến giao dịch không thành công.

  • Trong một số trường hợp cuộc gọi hợp đồng chéo, việc thực hiện hợp đồng phụ thuộc vào trạng thái thực hiện của các hợp đồng bên ngoài khác. Tuy nhiên, việc thực hiện các hợp đồng bên ngoài không phải lúc nào cũng đáng tin cậy, do đó việc thực hiện hợp đồng này có thể bị chặn bởi các hợp đồng bên ngoài và không thể hoạt động như bình thường. Sự xuất hiện của loại vấn đề này có thể được biểu hiện khi tiền của người dùng hợp đồng trong hợp đồng bị khóa, do đó không thể nạp hoặc rút tiền bình thường.

  • Ngoài những sai sót trong logic hợp đồng, nguyên nhân của hiện tượng DoS cũng có thể là do yếu tố con người: một ví dụ điển hình: chủ sở hữu hợp đồng bị mất khóa riêng của mình, do đó một số chức năng đặc quyền thực thi của only_owner trong hợp đồng không thể thực hiện được. bị gọi, khiến Một số giá trị trạng thái hệ thống quan trọng trong hợp đồng không thể cập nhật kịp thời, có thể gây tổn thất lớn hơn cho dự án.


tiêu đề phụ

1. Lặp lại cấu trúc dữ liệu có thể thay đổi bằng lệnh gọi bên ngoài

Sau đây là một hợp đồng thông minh đơn giản cho "cổ tức" cho người dùng đã đăng ký trong hợp đồng và dữ liệu trạng thái của nó như sau:

Người dùng có thể đăng ký và khởi tạo bằng cách gọi hàm pub fn register_account().

Sau đó, người quản lý hợp đồng sẽ gọi hàm pub fn phân phối_token để thực hiện phân phối cho người dùng trong hệ thống"cổ tức". Cách "cổ tức" là duyệt qua mảng người dùng self.registered và chuyển số lượng mã thông báo được chỉ định cho mỗi người dùng thông qua các cuộc gọi hợp đồng chéo dưới dạng phần thưởng.

Tuy nhiên, kích thước của dữ liệu trạng thái hợp đồng (self.registered) không bị giới hạn và có thể bị người dùng ác ý thao túng, khiến kích thước của dữ liệu hợp đồng trở nên quá lớn. Vì vậy, khi người dùng NHÀ PHÂN PHỐI gọi phương thức hợp đồng này, phí Gas có thể được tiêu thụ quá cao, vượt quá GIỚI HẠN GAS.

Sau đây là kết quả kiểm tra hợp đồng trong NEAR Localnet thực tế

Có thể thấy rằng khi có nhiều người dùng đã đăng ký trong hệ thống, bộ Prepreced_gas sẽ không đủ để đáp ứng các hoạt động chuyển tiền của tất cả người dùng trong quá trình thực hiện phân phối_token thực tế, do đó giao dịch này không thành công.

Giải pháp được đề nghị:

Do giới hạn của Gas Limit, không nên duyệt cấu trúc dữ liệu lớn trong quá trình thực hiện phương thức hợp đồng (kích thước của cấu trúc dữ liệu có thể được thao tác bởi người dùng bên ngoài). Nếu bạn thực sự cần phải duyệt qua, bạn cũng cần giới hạn kích thước của cấu trúc dữ liệu và đảm bảo rằng khi kích thước của cấu trúc dữ liệu đạt đến giá trị tối đa, nó sẽ không chạm vào giới hạn Gas Limit.

tiêu đề phụ

2. Sự phụ thuộc trạng thái giữa các hợp đồng chéo dẫn đến chặn hợp đồng

Khi một hợp đồng thực hiện cuộc gọi hợp đồng chéo, nó có thể phụ thuộc vào trạng thái của hợp đồng bên ngoài. Sự phụ thuộc không đúng cách sẽ khiến hợp đồng bị chặn, điều này có thể gây ra một cuộc tấn công DoS

Hãy xem xét một kịch bản trong đó hợp đồng thông minh được sử dụng để "đặt giá thầu":

Người dùng có thể đăng ký tài khoản bằng cách gọi phương thức hàm pub fn register_account trong "hợp đồng đấu thầu" để chuẩn bị tham gia đấu thầu tiếp theo

Người dùng cũng có thể truy vấn ID người dùng có giá thầu cao nhất cho đến nay trong hệ thống hiện tại và giá họ đặt giá thầu thông qua các chức năng giao diện sau.

Người dùng cũng có thể truy vấn ID người dùng có giá thầu cao nhất cho đến nay trong hệ thống hiện tại và giá họ đặt giá thầu thông qua các chức năng giao diện sau.

Khi hợp đồng đặt giá thầu nhận được mã thông báo, nó sẽ gọi chức năng giá thầu sau thông qua chức năng ft_on_transfer.

Trong chức năng đặt giá thầu này, logic thực thi của chức năng trước tiên sẽ kiểm tra xem giá thầu của người dùng hiện tại có cao hơn giá trị giá thầu của người trả giá cao nhất trước đó hay không. Nếu điều kiện được đáp ứng, self.refund_exe() sẽ được thực thi để trả lại mã thông báo giá thầu của người đặt giá thầu cao nhất trước đó từ "hợp đồng đặt giá thầu". Sau đó, cập nhật ID người dùng có giá thầu cao nhất cho đến nay và giá mà nó đặt giá thầu.

Tình hình thực tế là theo định nghĩa hợp lý của hợp đồng: mã thông báo giá thầu của người dùng có giá thầu cao nhất phải được trả lại để thay thế ID của người dùng có giá thầu cao nhất cho đến nay.

Trong chức năng đặt giá thầu này, logic thực thi của chức năng trước tiên sẽ kiểm tra xem giá thầu của người dùng hiện tại có cao hơn giá trị giá thầu của người trả giá cao nhất trước đó hay không. Nếu điều kiện được đáp ứng, self.refund_exe() sẽ được thực thi để trả lại mã thông báo giá thầu của người đặt giá thầu cao nhất trước đó từ "hợp đồng đặt giá thầu". Sau đó, cập nhật ID người dùng có giá thầu cao nhất cho đến nay và giá mà nó đặt giá thầu.

Tình hình thực tế là theo định nghĩa hợp lý của hợp đồng: mã thông báo giá thầu của người dùng có giá thầu cao nhất phải được trả lại để thay thế ID của người dùng có giá thầu cao nhất cho đến nay.

Tại thời điểm này, thử nghiệm mô phỏng những người dùng tham gia "hệ thống đặt giá thầu": user0, user1 và user2

Mỗi người có 10.000 mã thông báo ban đầu. user0 đặt giá thầu đầu tiên là 1000 trong "hệ thống đặt giá thầu". Tại thời điểm này, truy vấn hiển thị rằng current_leader: user0.test.nearhighest_bid: 1000. Sau đó, user0 ngay lập tức chuyển 9000 token còn lại cho user2 và hủy tài khoản token.

Sau đó, khi user1 đặt giá thầu 2000, hệ thống sẽ có kế hoạch trả lại giá trị giá thầu trước đó của người dùng0. Tuy nhiên, do lúc này tài khoản của user0 không còn tồn tại nên hệ thống sẽ nhắc"Cannot Refund", trạng thái cập nhật giao dịch tiếp theo không thể hoàn tất thành công.

Tại thời điểm này, nhà thầu thứ hai muốn đặt giá thầu 2000:

  • Giải pháp:

tiêu đề phụ

3. Mất khóa riêng của chủ sở hữu

Tập trung một phần thường tồn tại trong các dự án hợp đồng thông minh phi tập trung: ví dụ: có một chủ sở hữu hợp đồng. Việc thực thi một số chức năng hợp đồng được đặt để chỉ chủ sở hữu mới có thể thực thi được, được sử dụng để đặt và thay đổi giá trị của một số biến hệ thống chính trong hợp đồng. Chúng ta có thể gọi những hàm như vậy là hàm kiểu only_owner.

Ví dụ: đối với pub fn phân phối_token được xác định trong hợp đồng "cổ tức" ở trên, hàm này là hàm only_owner. Khi chủ sở hữu hợp đồng không thể thực hiện các chức năng của mình (khóa riêng tư bị mất), tiền sẽ luôn bị khóa trong hợp đồng và không thể phân phối cho người dùng khác. Trong hầu hết các trường hợp, hàm only_owner cũng có thể được sử dụng để tạm dừng hoặc khởi động lại tất cả các giao dịch trong hợp đồng, điều này cho thấy tầm quan trọng của chủ sở hữu trong việc thực hiện các chức năng của nó một cách bình thường.

  • Giải pháp:

Để tránh "khuyết tật" cá nhân nêu trên của chủ sở hữu, chúng tôi có thể thêm nhiều chủ sở hữu hợp đồng để cùng quản lý hợp đồng, thậm chí sử dụng phương pháp yêu cầu nhiều chữ ký để thay thế sơ đồ kiểm soát cơ quan hợp đồng ban đầu, để nhận ra sự phân cấp của hiệu lực quản trị hợp đồng. Việc thiết kế và triển khai chức năng yêu cầu đa chữ ký trong hợp đồng thông minh sẽ được mô tả chi tiết trong "Nhật ký phát triển hợp đồng thông minh" tiếp theo.

BlockSec
作者文库