Khuyến nghị bảo mật hợp đồng thông minh Ethereum

blog 1Tin tứcDevelopersEnterpriseBlockchain ExplainedEvents and ConferencePressBản tin

Contents

Theo dõi bản tin của chúng tôi.

Địa chỉ email

Chúng tôi tôn trọng quyền riêng tư của bạn

Trang chủBlogBlockchain phát triển

Khuyến nghị bảo mật hợp đồng thông minh Ethereum

Từ cách xử lý các cuộc gọi bên ngoài đến các kế hoạch cam kết, đây là hơn 10 mẫu bảo mật hợp đồng thông minh cần tuân theo khi bạn đang xây dựng trên Ethereum. Bởi ConsenSysJuly 10, 2020Đăng vào ngày 10 tháng 7 năm 2020

Khuyến nghị bảo mật hợp đồng thông minh Ethereum

Như chúng tôi đã đề cập trong Tư duy bảo mật hợp đồng thông minh, một nhà phát triển Ethereum thận trọng luôn ghi nhớ 5 nguyên tắc:

  • Chuẩn bị cho thất bại
  • Triển khai cẩn thận
  • Giữ hợp đồng đơn giản
  • Ở lại đến ngày
  • Lưu ý về các đặc điểm riêng của EVM

Trong bài đăng này, chúng ta sẽ đi sâu vào các đặc điểm riêng của EVM và xem qua danh sách các mẫu bạn nên tuân theo khi phát triển bất kỳ hệ thống hợp đồng thông minh nào trên Ethereum. Phần này chủ yếu dành cho các nhà phát triển Ethereum trung gian. Nếu bạn vẫn đang trong giai đoạn đầu khám phá, hãy xem chương trình nhà phát triển blockchain theo yêu cầu của Học viện ConsenSys. 

Được rồi, chúng ta hãy đi sâu vào.

Cuộc gọi bên ngoài

Thận trọng khi thực hiện các cuộc gọi bên ngoài

Các cuộc gọi đến các hợp đồng thông minh không đáng tin cậy có thể gây ra một số rủi ro hoặc lỗi không mong muốn. Các cuộc gọi bên ngoài có thể thực thi mã độc hại trong hợp đồng đó hoặc bất kỳ hợp đồng nào khác mà nó phụ thuộc vào. Do đó, hãy coi mọi cuộc gọi bên ngoài là một nguy cơ bảo mật tiềm ẩn. Khi không thể hoặc không muốn xóa các cuộc gọi bên ngoài, hãy sử dụng các khuyến nghị trong phần còn lại của phần này để giảm thiểu nguy hiểm.

Đánh dấu các hợp đồng không đáng tin cậy

Khi tương tác với các hợp đồng bên ngoài, hãy đặt tên cho các biến, phương thức và giao diện hợp đồng của bạn theo cách làm rõ rằng việc tương tác với chúng có khả năng không an toàn. Điều này áp dụng cho các chức năng của riêng bạn gọi các hợp đồng bên ngoài.

// bad Bank.withdraw (100); // Không rõ liệu hàm đáng tin cậy hay không đáng tin cậy makeWithdrawal (số tiền gợi ý) {// Không rõ rằng hàm này có khả năng không an toàn Bank.withdraw (số tiền); } // tốt UntrustedBank.withdraw (100); // lệnh gọi bên ngoài không đáng tin cậy TrustedBank.withdraw (100); // hợp đồng ngân hàng bên ngoài nhưng đáng tin cậy được duy trì bởi XYZ Corp function makeUntrustedWithdrawal (uint amount) {UntrustedBank.withdraw (số tiền); } Ngôn ngữ mã: PHP (php)

Tránh thay đổi trạng thái sau cuộc gọi bên ngoài

Cho dù sử dụng các lệnh gọi thô (có dạng someAddress.call ()) hay các lệnh gọi hợp đồng (dạng ExternalContract.someMethod ()), hãy giả sử rằng mã độc hại có thể thực thi. Ngay cả khi ExternalContract không độc hại, mã độc có thể được thực thi bởi bất kỳ hợp đồng nào mà nó gọi.

Một mối nguy hiểm cụ thể là mã độc hại có thể chiếm quyền điều khiển, dẫn đến lỗ hổng bảo mật. (Xem Gần đây để thảo luận đầy đủ hơn về vấn đề này).

Nếu bạn đang thực hiện cuộc gọi đến một hợp đồng bên ngoài không đáng tin cậy, hãy tránh thay đổi trạng thái sau cuộc gọi. Mẫu này đôi khi còn được gọi là mẫu kiểm tra-hiệu ứng-tương tác.

Xem SWC-107

Không sử dụng chuyển () hoặc gửi ().

.transfer () và .send () chuyển tiếp chính xác 2.300 gas cho người nhận. Mục tiêu của sự phụ thuộc vào khí được mã hóa cứng này là để ngăn chặn lỗ hổng bảo mật, nhưng điều này chỉ có ý nghĩa khi giả định rằng chi phí khí đốt là không đổi. EIP 1884, vốn là một phần của hard fork Istanbul, đã làm tăng chi phí xăng của hoạt động SLOAD. Điều này khiến hàm dự phòng của hợp đồng tiêu tốn hơn 2300 gas. Chúng tôi khuyên bạn nên ngừng sử dụng.transfer () và.send () và thay vào đó use.call ().

// hợp đồng không hợp lệ Dễ bị tổn thương {hàm rút (số tiền uint256) bên ngoài {// Điều này chuyển tiếp 2300 gas, có thể không đủ nếu người nhận // là một hợp đồng và chi phí gas thay đổi. msg.sender.transfer (số lượng); }} // hợp đồng tốt Cố định {chức năng rút (số tiền uint256) bên ngoài {// Điều này chuyển tiếp tất cả gas hiện có. Hãy chắc chắn để kiểm tra giá trị trả lại! (bool thành công,) = msg.sender.call.value (số tiền) (""); yêu cầu (thành công, "Chuyển không thành công."); }} Ngôn ngữ mã: JavaScript (javascript)

Lưu ý rằng.call () không làm gì để giảm thiểu các cuộc tấn công lần truy cập gần đây, vì vậy các biện pháp phòng ngừa khác phải được thực hiện. Để ngăn chặn các cuộc tấn công gần đây, hãy sử dụng mẫu kiểm tra-hiệu ứng-tương tác.

Xử lý lỗi trong cuộc gọi bên ngoài

Solidity cung cấp các phương thức gọi cấp thấp hoạt động trên các địa chỉ thô: address.call (), address.callcode (), address.delegatecall () và address.send (). Các phương thức cấp thấp này không bao giờ ném một ngoại lệ, nhưng sẽ trả về false nếu cuộc gọi gặp một ngoại lệ. Mặt khác, các lệnh gọi hợp đồng (ví dụ: ExternalContract.doSomething ()) sẽ tự động truyền một lượt ném (ví dụ: ExternalContract.doSomething () cũng sẽ ném nếu doSomething () ném).

Nếu bạn chọn sử dụng các phương thức gọi cấp thấp, hãy đảm bảo xử lý khả năng cuộc gọi thất bại, bằng cách kiểm tra giá trị trả về.

// bad someAddress.send (55); someAddress.call.value (55) (""); // điều này nguy hiểm gấp đôi, vì nó sẽ chuyển tiếp tất cả khí còn lại và không kiểm tra kết quả someAddress.call.value (100) (bytes4 (sha3 ("tiền gửi()"))); // nếu khoản tiền gửi ném ra một ngoại lệ, thì lệnh gọi raw () sẽ chỉ trả về false và giao dịch sẽ KHÔNG được hoàn nguyên // tốt (bool thành công,) = someAddress.call.value (55) (""); if (! thành công) {// xử lý mã lỗi} ExternalContract (someAddress) .deposit.value (100) (); Ngôn ngữ mã: JavaScript (javascript)

Xem SWC-104

Ưu đãi kéo qua thúc đẩy cho các cuộc gọi bên ngoài

Các cuộc gọi bên ngoài có thể thất bại vô tình hoặc cố ý. Để giảm thiểu thiệt hại do những lỗi như vậy gây ra, tốt hơn hết là cô lập từng cuộc gọi bên ngoài thành giao dịch của chính nó mà người nhận cuộc gọi có thể bắt đầu. Điều này đặc biệt liên quan đến các khoản thanh toán, nơi tốt hơn là để người dùng rút tiền thay vì tự động đẩy tiền cho họ. (Điều này cũng làm giảm cơ hội vấn đề với giới hạn khí.) Tránh kết hợp nhiều lần chuyển ether trong một giao dịch duy nhất.

// đấu giá hợp đồng không hợp đồng {địa chỉ cao nhấtBidder; uint cao nhấtBid; giá thầu hàm () phải trả {request (msg.value >= cao nhất); if (cao nhấtBidder! = địa chỉ (0)) {(bool thành công,) = cao nhấtBidder.call.value (giá thầu cao nhất) (""); yêu cầu (thành công); // nếu lệnh gọi này liên tục không thành công, không ai khác có thể đặt giá thầu} mostBidder = msg.sender; highBid = msg.value; }} // đấu giá hợp đồng tốt {địa chỉ highBidder; uint cao nhấtBid; ánh xạ (địa chỉ => uint) hoàn lại tiền; giá thầu hàm () phải trả bên ngoài {request (msg.value >= cao nhất); if (mostBidder! = address (0)) {hoàn lại tiền [mostBidder] + = cao nhấtBid; // ghi lại khoản tiền hoàn lại mà người dùng này có thể yêu cầu} mostBidder = msg.sender; highBid = msg.value; } function rút lui () bên ngoài {uint hoàn trả = tiền hoàn lại [msg.sender]; tiền hoàn lại [msg.sender] = 0; (bool thành công,) = msg.sender.call.value (hoàn lại tiền) (""); yêu cầu (thành công); }} Ngôn ngữ mã: JavaScript (javascript)

Xem SWC-128

Đừng ủy quyền cho mã không đáng tin cậy

Hàm ủy quyền gọi các chức năng từ các hợp đồng khác như thể chúng thuộc về hợp đồng người gọi. Do đó, callee có thể thay đổi trạng thái của địa chỉ đang gọi. Điều này có thể không an toàn. Một ví dụ dưới đây cho thấy việc sử dụng ủy quyền có thể dẫn đến việc phá hủy hợp đồng và mất số dư của nó như thế nào.

Contract Destructor {function doWork () external {selfdestruct (0); }} contract Worker {function doWork (address _internalWorker) public {// không an toàn _internalWorker.delegatecall (bytes4 (keccak256 ("làm việc()"))); }} Ngôn ngữ mã: JavaScript (javascript)

Nếu Worker.doWork () được gọi với địa chỉ của hợp đồng Kẻ hủy đã triển khai làm đối số, thì hợp đồng Người lao động sẽ tự hủy. Chỉ ủy quyền thực thi cho các hợp đồng đáng tin cậy và không bao giờ đến một địa chỉ do người dùng cung cấp.

Cảnh báo

Đừng cho rằng các hợp đồng được tạo với số dư bằng không. Kẻ tấn công có thể gửi ether đến địa chỉ của hợp đồng trước khi nó được tạo. Các hợp đồng không nên cho rằng trạng thái ban đầu của nó chứa số dư bằng không. Xem vấn đề 61 để biết thêm chi tiết.

Xem SWC-112

Hãy nhớ rằng ether có thể được gửi một cách cưỡng bức vào một tài khoản

Cẩn thận với việc mã hóa một bất biến kiểm tra chặt chẽ số dư của một hợp đồng.

Kẻ tấn công có thể cưỡng bức gửi ether đến bất kỳ tài khoản nào. Điều này không thể được ngăn chặn (ngay cả với một hàm dự phòng thực hiện hoàn nguyên ()).

Kẻ tấn công có thể thực hiện điều này bằng cách tạo một hợp đồng, cấp vốn cho nó bằng 1 wei và gọi tự hủy cấu trúc (địa chỉ nạn nhân). Không có mã nào được gọi trong địa chỉ nạn nhân, vì vậy nó không thể bị ngăn chặn. Điều này cũng đúng với phần thưởng khối được gửi đến địa chỉ của người khai thác, có thể là bất kỳ địa chỉ tùy ý nào.

Ngoài ra, vì địa chỉ hợp đồng có thể được tính toán trước, ether có thể được gửi đến một địa chỉ trước khi hợp đồng được triển khai.

Xem SWC-132

Hãy nhớ rằng dữ liệu trên chuỗi là công khai

Nhiều ứng dụng yêu cầu dữ liệu đã gửi phải ở chế độ riêng tư cho đến một thời điểm nào đó để có thể hoạt động. Trò chơi (ví dụ: oẳn tù tì trên dây chuyền) và cơ chế đấu giá (ví dụ: đấu giá kín Đấu giá Vickrey) là hai loại ví dụ chính. Nếu bạn đang xây dựng một ứng dụng mà quyền riêng tư là một vấn đề, hãy đảm bảo rằng bạn tránh yêu cầu người dùng xuất bản thông tin quá sớm. Chiến lược tốt nhất là sử dụng chương trình cam kết với các giai đoạn riêng biệt: cam kết đầu tiên bằng cách sử dụng băm của các giá trị và trong giai đoạn sau tiết lộ các giá trị.

Ví dụ:

  • Trong trò oẳn tù tì, trước tiên yêu cầu cả hai người chơi nộp một số băm của nước đi dự định, sau đó yêu cầu cả hai người chơi nộp nước đi của họ; nếu nước đi đã gửi không khớp với hàm băm, hãy ném nó ra ngoài.
  • Trong một cuộc đấu giá, yêu cầu người chơi gửi một hàm băm của giá trị giá thầu của họ trong giai đoạn đầu (cùng với một khoản tiền gửi lớn hơn giá trị giá thầu của họ), sau đó gửi giá trị đấu giá của họ trong giai đoạn thứ hai.
  • Khi phát triển một ứng dụng phụ thuộc vào trình tạo số ngẫu nhiên, thứ tự phải luôn là (1) người chơi gửi nước đi, (2) số ngẫu nhiên được tạo, (3) người chơi trả tiền. Nhiều người đang tích cực nghiên cứu máy phát số ngẫu nhiên; các giải pháp tốt nhất hiện tại bao gồm tiêu đề khối Bitcoin (được xác minh thông qua http://btcrelay.org), lược đồ băm-cam kết-tiết lộ (tức là một bên tạo ra một số, xuất bản hàm băm của nó để “cam kết” với giá trị, rồi tiết lộ giá trị sau đó) và RANDAO. Vì Ethereum là một giao thức xác định, bạn không thể sử dụng bất kỳ biến nào trong giao thức như một số ngẫu nhiên không thể đoán trước. Cũng xin lưu ý rằng các thợ đào ở một mức độ nào đó có quyền kiểm soát giá trị block.blockhash ()*.

Cẩn thận với khả năng một số người tham gia có thể “bỏ mạng” và không quay lại

Không thực hiện quy trình hoàn trả hoặc yêu cầu bồi thường phụ thuộc vào một bên cụ thể thực hiện một hành động cụ thể mà không có cách nào khác để lấy tiền ra. Ví dụ, trong trò chơi oẳn tù tì, một sai lầm phổ biến là không thực hiện thanh toán cho đến khi cả hai người chơi cùng nộp nước đi; tuy nhiên, một người chơi ác ý có thể “làm đau lòng” người kia bằng cách đơn giản là không bao giờ thực hiện nước đi của họ – trên thực tế, nếu một người chơi nhìn thấy nước đi bị lộ của người chơi kia và xác định rằng họ đã thua, họ không có lý do gì để đưa ra nước đi của mình. Vấn đề này cũng có thể nảy sinh trong bối cảnh giải quyết kênh nhà nước. Khi những tình huống như vậy là một vấn đề, (1) cung cấp một cách để phá vỡ những người tham gia không tham gia, có thể thông qua một thời hạn và (2) xem xét thêm một động lực kinh tế bổ sung để những người tham gia gửi thông tin trong tất cả các tình huống mà họ phải làm như vậy.

Cẩn thận với phủ định của số nguyên có dấu âm nhất

Solidity cung cấp một số kiểu để làm việc với các số nguyên có dấu. Giống như trong hầu hết các ngôn ngữ lập trình, trong Solidity, một số nguyên có dấu với N bit có thể biểu diễn các giá trị từ -2 ^ (N-1) đến 2 ^ (N-1) -1. Điều này có nghĩa là không có giá trị tương đương dương nào cho MIN_INT. Phủ định được thực hiện khi tìm phần bù của hai số, vì vậy phủ định của số âm nhất sẽ dẫn đến cùng một số. Điều này đúng với tất cả các kiểu số nguyên có dấu trong Solidity (int8, int16,…, int256).

hợp đồng Phủ định {function negate8 (int8 _i) public pure return (int8) {return -_i; } function negate16 (int16 _i) public pure return (int16) {return -_i; } int8 public a = negate8 (-128); // -128 int16 public b = negate16 (-128); // 128 int16 public c = negate16 (-32768); // -32768} Ngôn ngữ mã: PHP (php)

Một cách để xử lý điều này là kiểm tra giá trị của một biến trước khi phủ định và ném nếu nó bằng MIN_INT. Một tùy chọn khác là đảm bảo rằng số âm nhất sẽ không bao giờ đạt được bằng cách sử dụng loại có dung lượng cao hơn (ví dụ: int32 thay vì int16).

Sự cố tương tự với kiểu int xảy ra khi MIN_INT được nhân hoặc chia cho -1.

Mã blockchain của bạn có an toàn không? 

Chúng tôi hy vọng những khuyến nghị này hữu ích. Nếu bạn và nhóm của bạn đang chuẩn bị ra mắt hoặc thậm chí ở giai đoạn đầu của vòng đời phát triển và cần kiểm tra các hợp đồng thông minh của mình, vui lòng liên hệ với nhóm kỹ sư bảo mật của chúng tôi tại ConsenSys Diligence. Chúng tôi ở đây để giúp bạn khởi chạy và duy trì các ứng dụng Ethereum của mình với sự tự tin 100%. 

Đặt kiểm tra điểm an ninh

Đặt một đánh giá trong 1 ngày với nhóm chuyên gia bảo mật blockchain của chúng tôi. Đặt trước cho bạn ngay hôm nay Bảo mật Hợp đồng thông minh Bản tin Đăng ký nhận bản tin của chúng tôi để biết tin tức mới nhất về Ethereum, giải pháp doanh nghiệp, tài nguyên dành cho nhà phát triển và hơn thế nữa.Cách xây dựng một sản phẩm chuỗi khối thành côngHội thảo trên web

Cách xây dựng một sản phẩm chuỗi khối thành công

Cách thiết lập và chạy Ethereum NodeHội thảo trên web

Cách thiết lập và chạy Ethereum Node

Cách xây dựng API Ethereum của riêng bạnHội thảo trên web

Cách xây dựng API Ethereum của riêng bạn

Cách tạo mã thông báo xã hộiHội thảo trên web

Cách tạo mã thông báo xã hội

Sử dụng các công cụ bảo mật trong phát triển hợp đồng thông minhHội thảo trên web

Sử dụng các công cụ bảo mật trong phát triển hợp đồng thông minh

Tương lai của tài sản kỹ thuật số tài chính và DeFiHội thảo trên web

Tương lai của tài chính: Tài sản kỹ thuật số và DeFi

Mike Owergreen Administrator
Sorry! The Author has not filled his profile.
follow me
Like this post? Please share to your friends:
Adblock
detector
map