Các phương pháp hay nhất về liên đới để bảo mật hợp đồng thông minh

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

Các phương pháp hay nhất về liên đới để bảo mật hợp đồng thông minh

Từ việc giám sát đến cân nhắc dấu thời gian, đây là một số mẹo chuyên nghiệp để đảm bảo hợp đồng thông minh Ethereum của bạn được củng cố. bởi ConsenSys Ngày 21 tháng 8 năm 2020Đăng vào ngày 21 tháng 8 năm 2020

anh hùng thực hành tốt nhất solidity

Bởi ConsenSys Diligence, nhóm chuyên gia bảo mật blockchain của chúng tôi.

Nếu bạn đã chú trọng đến tư duy bảo mật của hợp đồng thông minh và đang xử lý các đặc điểm riêng của EVM, thì đã đến lúc xem xét một số mẫu bảo mật dành riêng cho ngôn ngữ lập trình Solidity. Trong phần tổng hợp này, chúng tôi sẽ tập trung vào các đề xuất phát triển an toàn cho Solidity, cũng có thể mang tính hướng dẫn để phát triển các hợp đồng thông minh bằng các ngôn ngữ khác. 

Được rồi, hãy bắt tay vào.

Sử dụng khẳng định (), yêu cầu (), hoàn nguyên () đúng cách

Các chức năng tiện lợi khẳng định và yêu cầu có thể được sử dụng để kiểm tra các điều kiện và đưa ra một ngoại lệ nếu điều kiện không được đáp ứng.

Các khẳng định hàm chỉ nên được sử dụng để kiểm tra các lỗi nội bộ và kiểm tra các bất biến.

Các yêu cầu hàm nên được sử dụng để đảm bảo các điều kiện hợp lệ, chẳng hạn như đầu vào hoặc các biến trạng thái hợp đồng được đáp ứng hoặc để xác thực các giá trị trả về từ các lệnh gọi đến các hợp đồng bên ngoài. 

Việc tuân theo mô hình này cho phép các công cụ phân tích chính thức xác minh rằng không bao giờ có thể tiếp cận được mã opcode không hợp lệ: nghĩa là không có bất biến nào trong mã bị vi phạm và mã được xác minh chính thức.

độ rắn pragma ^ 0,5,0; hợp đồng Sharer {function sendHalf (địa chỉ có thể trả thêm) lợi nhuận công khai phải trả (số dư uint) {request (msg.value% 2 == 0, "Giá trị chẵn bắt buộc."); // Request () có thể có một chuỗi thông báo tùy chọn uint balanceBeforeTransfer = address (this) .balance; (bool thành công,) = addr.call.value (msg.value / 2) (""); yêu cầu (thành công); // Vì chúng tôi đã hoàn nguyên nếu chuyển không thành công, nên // không có cách nào để chúng tôi vẫn còn một nửa số tiền. khẳng định (địa chỉ (this) .balance == balanceBeforeTransfer – msg.value / 2); // được sử dụng để kiểm tra lỗi nội bộ địa chỉ trả về (this) .balance; }} Ngôn ngữ mã: JavaScript (javascript)

Xem SWC-110 & SWC-123

Chỉ sử dụng công cụ sửa đổi để kiểm tra

Mã bên trong một công cụ sửa đổi thường được thực thi trước phần thân hàm, vì vậy bất kỳ thay đổi trạng thái nào hoặc các lệnh gọi bên ngoài sẽ vi phạm Kiểm tra-Hiệu ứng-Tương tác mẫu. Hơn nữa, các câu lệnh này cũng có thể không được nhà phát triển chú ý, vì mã cho công cụ sửa đổi có thể khác xa với phần khai báo hàm. Ví dụ: một lệnh gọi bên ngoài trong công cụ sửa đổi có thể dẫn đến cuộc tấn công lần truy cập gần đây:

hợp đồng Registry {chủ sở hữu địa chỉ; function isVoter (address _addr) external return (bool) {// Code}} hợp đồng Bầu cử {Registry registry; modifier isE Đủ điều kiện (address _addr) {request (registry.isVoter (_addr)); _; } function vote () isE Đủ điều kiện (msg.sender) public {// Code}} Ngôn ngữ mã: JavaScript (javascript)

Trong trường hợp này, hợp đồng đăng ký có thể thực hiện một cuộc tấn công tái cấp quyền bằng cách gọi Election.vote () bên trong isVoter ().

Ghi chú: Sử dụng bổ ngữ để thay thế các kiểm tra điều kiện trùng lặp trong nhiều hàm, chẳng hạn như isOwner (), nếu không, hãy sử dụng lệnh request hoặc hoàn nguyên bên trong hàm. Điều này làm cho mã hợp đồng thông minh của bạn dễ đọc hơn và dễ kiểm tra hơn.

Cẩn thận làm tròn với phép chia số nguyên

Tất cả phép chia số nguyên làm tròn xuống số nguyên gần nhất. Nếu bạn cần độ chính xác cao hơn, hãy cân nhắc sử dụng hệ số nhân hoặc lưu trữ cả tử số và mẫu số.

(Trong tương lai, Solidity sẽ có một điểm cố định nhập, điều này sẽ làm cho việc này dễ dàng hơn.)

// uint xấu x = 5/2; // Kết quả là 2, tất cả các số nguyên làm tròn DOWN thành số nguyên gần nhất Ngôn ngữ mã: JavaScript (javascript)

Việc sử dụng hệ số nhân ngăn chặn việc làm tròn xuống, hệ số nhân này cần được tính đến khi làm việc với x trong tương lai:

// tốt uint nhân = 10; uint x = (5 * số nhân) / 2; Ngôn ngữ mã: JavaScript (javascript)

Lưu trữ tử số và mẫu số có nghĩa là bạn có thể tính toán kết quả của tử số / mẫu số ngoài chuỗi:

// tử số uint tốt = 5; mẫu số uint = 2; Ngôn ngữ mã: JavaScript (javascript)

Nhận thức được sự cân bằng giữa hợp đồng trừu tượng và giao diện

Cả hai giao diện và hợp đồng trừu tượng đều cung cấp một phương pháp tiếp cận có thể tùy chỉnh và tái sử dụng cho các hợp đồng thông minh. Các giao diện, được giới thiệu trong Solidity 0.4.11, tương tự như các hợp đồng trừu tượng nhưng không thể có bất kỳ chức năng nào được triển khai. Các giao diện cũng có những hạn chế như không thể truy cập vào bộ nhớ hoặc kế thừa từ các giao diện khác, điều này thường làm cho các hợp đồng trừu tượng trở nên thực tế hơn. Mặc dù, các giao diện chắc chắn hữu ích cho việc thiết kế các hợp đồng trước khi thực hiện. Ngoài ra, điều quan trọng cần lưu ý là nếu một hợp đồng kế thừa từ một hợp đồng trừu tượng thì nó phải triển khai tất cả các chức năng không được triển khai thông qua ghi đè hoặc nó cũng sẽ trừu tượng..

Các chức năng dự phòng

Giữ các chức năng dự phòng đơn giản

Các chức năng dự phòng được gọi khi hợp đồng được gửi một thông báo không có đối số (hoặc khi không có hàm nào phù hợp) và chỉ có quyền truy cập vào 2.300 gas khi được gọi từ .send () hoặc .transfer (). Nếu bạn muốn có thể nhận Ether từ .send () hoặc .transfer (), thì điều bạn có thể làm trong hàm dự phòng là ghi lại một sự kiện. Sử dụng một chức năng thích hợp nếu cần tính toán nhiều khí hơn.

// bad function () Pay {balances [msg.sender] + = msg.value; } // chức năng tốt tiền gửi () phải trả bên ngoài {balances [msg.sender] + = msg.value; } function () pay {request (msg.data.length == 0); phát ra LogDepositReceive (msg.sender); } Ngôn ngữ mã: JavaScript (javascript)

Kiểm tra độ dài dữ liệu trong các hàm dự phòng

Kể từ khi chức năng dự phòng không chỉ được gọi để chuyển ether thuần túy (không có dữ liệu) mà còn khi không có hàm nào khác khớp, bạn nên kiểm tra xem dữ liệu có trống không nếu hàm dự phòng chỉ được sử dụng cho mục đích ghi lại Ether đã nhận. Nếu không, người gọi sẽ không nhận thấy nếu hợp đồng của bạn được sử dụng không đúng cách và các chức năng không tồn tại sẽ được gọi.

// bad function () Pay {release LogDepositReceive (msg.sender); } // hàm tốt () phải trả {request (msg.data.length == 0); phát ra LogDepositReceive (msg.sender); } Ngôn ngữ mã: JavaScript (javascript)

Đánh dấu rõ ràng các hàm phải trả và các biến trạng thái

Bắt đầu từ Solidity 0.4.0, mọi chức năng đang nhận ether phải sử dụng công cụ sửa đổi phải trả, nếu không nếu giao dịch có msg.value > 0 sẽ hoàn nguyên (ngoại trừ khi bị ép buộc).

Ghi chú: Điều gì đó có thể không rõ ràng: Công cụ sửa đổi phải trả chỉ áp dụng cho các cuộc gọi từ các hợp đồng bên ngoài. Nếu tôi gọi một hàm không phải trả trong hàm phải trả trong cùng một hợp đồng, thì hàm không phải trả sẽ không bị lỗi, mặc dù msg.value vẫn được đặt.

Đánh dấu rõ ràng khả năng hiển thị trong các hàm và biến trạng thái

Gắn nhãn rõ ràng khả năng hiển thị của các hàm và biến trạng thái. Các chức năng có thể được chỉ định là bên ngoài, công khai, nội bộ hoặc riêng tư. Vui lòng hiểu sự khác biệt giữa chúng, ví dụ: bên ngoài có thể là đủ thay vì công khai. Đối với các biến trạng thái, bên ngoài là không thể. Việc gắn nhãn khả năng hiển thị một cách rõ ràng sẽ giúp bạn dễ dàng nhận ra các giả định không chính xác về người có thể gọi hàm hoặc truy cập vào biến.

  • Các chức năng bên ngoài là một phần của giao diện hợp đồng. Một hàm bên ngoài f không thể được gọi trong nội bộ (tức là f () không hoạt động, nhưng hàm this.f () hoạt động). Các chức năng bên ngoài đôi khi hiệu quả hơn khi chúng nhận được các mảng dữ liệu lớn.
  • Các chức năng công khai là một phần của giao diện hợp đồng và có thể được gọi nội bộ hoặc thông qua tin nhắn. Đối với các biến trạng thái công khai, một hàm getter tự động (xem bên dưới) được tạo.
  • Các hàm nội bộ và biến trạng thái chỉ có thể được truy cập nội bộ mà không cần sử dụng.
  • Các hàm riêng và các biến trạng thái chỉ hiển thị cho hợp đồng mà chúng được định nghĩa và không hiển thị trong các hợp đồng dẫn xuất. Ghi chú: Mọi thứ bên trong hợp đồng đều hiển thị cho tất cả những người quan sát bên ngoài blockchain, ngay cả các biến Riêng tư.

// uint x; // mặc định là nội bộ đối với các biến trạng thái, nhưng nó phải được đặt là hàm rõ ràng buy () {// mặc định là public // public code} // good uint private y; function buy () external {// chỉ có thể gọi bên ngoài hoặc sử dụng this.buy ()} function tiện ích () public {// có thể gọi bên ngoài, cũng như bên trong: việc thay đổi mã này đòi hỏi phải suy nghĩ về cả hai trường hợp. } function internalAction () internal {// internal code} Ngôn ngữ mã: PHP (php)

Xem SWC-100 và SWC-108

Khóa pragmas thành phiên bản trình biên dịch cụ thể

Các hợp đồng nên được triển khai với cùng một phiên bản trình biên dịch và các cờ mà chúng đã được thử nghiệm nhiều nhất. Khóa pragma giúp đảm bảo rằng các hợp đồng không vô tình được triển khai bằng cách sử dụng, chẳng hạn như trình biên dịch mới nhất có thể có rủi ro cao hơn về các lỗi chưa được phát hiện. Các hợp đồng cũng có thể được triển khai bởi những người khác và pragma chỉ ra phiên bản trình biên dịch mà các tác giả ban đầu dự định.

// độ rắn pragma xấu ^ 0.4.4; // good pragma solidity 0.4.4; Ngôn ngữ mã: JavaScript (javascript)

Lưu ý: phiên bản pragma nổi (tức là. ^ 0.4.25) sẽ biên dịch tốt với 0.4.26-nightly.2018.9.25, tuy nhiên bạn không nên sử dụng các bản dựng hàng đêm để biên dịch mã cho sản xuất.

Cảnh báo: Các câu lệnh Pragma có thể được phép thả nổi khi một hợp đồng được các nhà phát triển khác dự định sử dụng, như trong trường hợp với các hợp đồng trong thư viện hoặc gói EthPM. Nếu không, nhà phát triển sẽ cần cập nhật pragma theo cách thủ công để biên dịch cục bộ.

Xem SWC-103

Sử dụng các sự kiện để giám sát hoạt động hợp đồng

Có thể hữu ích nếu có một cách để theo dõi hoạt động của hợp đồng sau khi nó được triển khai. Một cách để thực hiện điều này là xem xét tất cả các giao dịch của hợp đồng, tuy nhiên điều đó có thể không đủ, vì các cuộc gọi tin nhắn giữa các hợp đồng không được ghi lại trong blockchain. Hơn nữa, nó chỉ hiển thị các tham số đầu vào, không hiển thị các thay đổi thực tế đang được thực hiện đối với trạng thái. Ngoài ra, các sự kiện có thể được sử dụng để kích hoạt các chức năng trong giao diện người dùng.

hợp đồng Từ thiện {mapping (địa chỉ => uint) số dư; function donate () phải trả cho public {balances [msg.sender] + = msg.value; }} hợp đồng Trò chơi {function buyCoins () phải trả công khai {// 5% được chuyển đến quỹ từ thiệnrity.donate.value (msg.value / 20) (); }} Ngôn ngữ mã: JavaScript (javascript)

Tại đây, Hợp đồng trò chơi sẽ thực hiện một cuộc gọi nội bộ đến Charity.donate (). Giao dịch này sẽ không xuất hiện trong danh sách giao dịch bên ngoài của Tổ chức từ thiện mà chỉ hiển thị trong các giao dịch nội bộ.

Một sự kiện là một cách thuận tiện để ghi lại điều gì đó đã xảy ra trong hợp đồng. Các sự kiện được phát ra vẫn nằm trong chuỗi khối cùng với dữ liệu hợp đồng khác và chúng có sẵn để kiểm tra trong tương lai. Đây là một cải tiến cho ví dụ ở trên, sử dụng các sự kiện để cung cấp lịch sử về các khoản đóng góp của Tổ chức từ thiện.

hợp đồng Từ thiện {// xác định sự kiện sự kiện LogDonate (uint _amount); ánh xạ (địa chỉ => uint) số dư; function donate () phải trả cho public {balances [msg.sender] + = msg.value; // sự kiện phát ra release LogDonate (msg.value); }} hợp đồng Trò chơi {function buyCoins () phải trả công khai {// 5% được chuyển đến quỹ từ thiệnrity.donate.value (msg.value / 20) (); }} Ngôn ngữ mã: JavaScript (javascript)

Tại đây, tất cả các giao dịch thông qua hợp đồng Từ thiện, dù trực tiếp hoặc không, sẽ hiển thị trong danh sách sự kiện của hợp đồng đó cùng với số tiền quyên góp.

Lưu ý: Ưu tiên các cấu trúc Solidity mới hơn. Ưu tiên các cấu trúc / bí danh như selfdestruct (khi tự sát) và keccak256 (trên sha3). Các mẫu như request (msg.sender.send (1 ether)) cũng có thể được đơn giản hóa bằng cách sử dụng transfer (), như trong msg.sender.transfer (1 ether). Thủ tục thanh toán Nhật ký thay đổi độ rắn để biết thêm những thay đổi tương tự.

Lưu ý rằng ‘Tích hợp sẵn’ có thể bị che

Hiện tại có thể bóng các khối cầu tích hợp sẵn trong Solidity. Điều này cho phép các hợp đồng ghi đè chức năng của các cài sẵn như msg và revert (). Mặc dù điều này được dự định, nó có thể đánh lừa người dùng hợp đồng về hành vi thực sự của hợp đồng.

hợp đồng Hợp đồng giả vờToRevert {hàm revert () hằng số nội bộ {}} hợp đồng Ví dụ: Hợp đồng hợp đồng là giả vờToRevert {function somethingBad () public {revert (); }}

Người dùng hợp đồng (và người đánh giá) nên biết mã nguồn hợp đồng thông minh đầy đủ của bất kỳ ứng dụng nào họ định sử dụng.

Tránh sử dụng tx.origin

Không bao giờ sử dụng tx.origin để ủy quyền, một hợp đồng khác có thể có một phương thức sẽ gọi hợp đồng của bạn (ví dụ như người dùng có một số tiền) và hợp đồng của bạn sẽ ủy quyền giao dịch đó vì địa chỉ của bạn nằm trong tx.origin.

hợp đồng MyContract {chủ sở hữu địa chỉ; function MyContract () public {owner = msg.sender; } function sendTo (địa chỉ nhận, số tiền uint) public {request (tx.origin == owner); (bool thành công,) = receiver.call.value (số tiền) (""); yêu cầu (thành công); }} hợp đồng AttackingContract {MyContract myContract; kẻ tấn công địa chỉ; function AttackingContract (địa chỉ myContractAddress) public {myContract = MyContract (myContractAddress); kẻ tấn công = msg.sender; } function () public {myContract.sendTo (attacker, msg.sender.balance); }} Ngôn ngữ mã: JavaScript (javascript)

Bạn nên sử dụng msg.sender để ủy quyền (nếu hợp đồng khác gọi hợp đồng của bạn thì msg.sender sẽ là địa chỉ của hợp đồng chứ không phải địa chỉ của người dùng đã gọi hợp đồng).

Bạn có thể đọc thêm về nó ở đây: Tài liệu về độ vững chắc

Cảnh báo: Ngoài vấn đề về ủy quyền, có khả năng tx.origin sẽ bị xóa khỏi giao thức Ethereum trong tương lai, vì vậy mã sử dụng tx.origin sẽ không tương thích với các bản phát hành trong tương lai Vitalik: “KHÔNG cho rằng tx.origin sẽ tiếp tục có thể sử dụng được hoặc có ý nghĩa.”

Cũng cần nhắc lại rằng bằng cách sử dụng tx.origin, bạn đang hạn chế khả năng tương tác giữa các hợp đồng vì hợp đồng sử dụng tx.origin không thể được sử dụng bởi một hợp đồng khác vì một hợp đồng không thể là tx.origin.

Xem SWC-115

Sự phụ thuộc vào dấu thời gian

Có ba cân nhắc chính khi sử dụng dấu thời gian để thực hiện một chức năng quan trọng trong hợp đồng, đặc biệt khi các hành động liên quan đến chuyển tiền.

Thao tác dấu thời gian

Cần biết rằng dấu thời gian của khối có thể bị thao túng bởi thợ đào. Xem xét điều này hợp đồng:

uint256 hằng số muối riêng = block.timestamp; function random (uint Max) hằng số trả về private (kết quả uint256) {// lấy hạt giống tốt nhất cho tính ngẫu nhiên uint256 x = salt * 100 / Max; uint256 y = salt * block.number / (muối% 5); uint256 seed = block.number / 3 + (salt% 300) + Last_Payout + y; uint256 h = uint256 (block.blockhash (hạt giống)); trả về uint256 ((h / x))% Max + 1; // số ngẫu nhiên từ 1 đến Max} Ngôn ngữ mã: PHP (php)

Khi hợp đồng sử dụng dấu thời gian để tạo ra một số ngẫu nhiên, người khai thác thực sự có thể đăng dấu thời gian trong vòng 15 giây kể từ khi khối được xác thực, cho phép người khai thác tính toán trước một lựa chọn có lợi hơn cho cơ hội của họ trong xổ số. Dấu thời gian không phải là ngẫu nhiên và không nên được sử dụng trong ngữ cảnh đó.

Quy tắc 15 giây

Các Giấy vàng (Đặc điểm kỹ thuật tham chiếu của Ethereum) không chỉ định ràng buộc về lượng khối có thể trôi theo thời gian, nhưng nó chỉ định rằng mỗi dấu thời gian phải lớn hơn dấu thời gian của mẹ của nó. Các triển khai giao thức Ethereum phổ biến Geth và Ngang bằng cả hai khối từ chối có dấu thời gian hơn 15 giây trong tương lai. Do đó, một nguyên tắc chung khi đánh giá việc sử dụng dấu thời gian là: nếu quy mô của sự kiện phụ thuộc vào thời gian của bạn có thể thay đổi trong 15 giây và duy trì tính toàn vẹn, thì sẽ an toàn khi sử dụng block.timestamp..

Tránh sử dụng block.number làm dấu thời gian

Có thể ước tính delta thời gian bằng cách sử dụng thuộc tính block.number và thời gian khối trung bình, tuy nhiên đây không phải là bằng chứng trong tương lai vì thời gian chặn có thể thay đổi (chẳng hạn như tổ chức lại ngã ba và bom khó). Trong một đợt giảm giá kéo dài nhiều ngày, quy tắc 15 giây cho phép một người đạt được ước tính thời gian đáng tin cậy hơn.

Xem SWC-116

Thận trọng về nhiều thừa kế

Khi sử dụng đa kế thừa trong Solidity, điều quan trọng là phải hiểu cách trình biên dịch soạn biểu đồ kế thừa.

hợp đồng Final {uint public a; function Final (uint f) public {a = f; }} hợp đồng B là Phí cuối cùng {int public; function B (uint f) Final (f) public {} function setFee () public {fee = 3; }} hợp đồng C là Phí cuối cùng {int public; function C (uint f) Final (f) public {} function setFee () public {fee = 5; }} hợp đồng A là B, C {function A () public B (3) C (5) {setFee (); }} Ngôn ngữ mã: PHP (php)

Khi một hợp đồng được triển khai, trình biên dịch sẽ tuyến tính hóa kế thừa từ phải sang trái (sau khi từ khóa là cha mẹ được liệt kê từ giống cơ sở nhất đến có nguồn gốc nhất). Đây là tuyến tính hóa của hợp đồng A:

Sau cùng <- B <- C <- A

Hệ quả của việc tuyến tính hóa sẽ mang lại giá trị phí là 5, vì C là hợp đồng có nguồn gốc nhiều nhất. Điều này có vẻ hiển nhiên, nhưng hãy tưởng tượng các tình huống trong đó C có thể phủ bóng các hàm quan trọng, sắp xếp lại các mệnh đề boolean và khiến nhà phát triển viết các hợp đồng có thể khai thác. Phân tích tĩnh hiện không đặt ra vấn đề với các chức năng bị lu mờ, vì vậy nó phải được kiểm tra thủ công.

Để giúp đóng góp, Solidity’s Github có dự án với tất cả các vấn đề liên quan đến thừa kế.

Xem SWC-125

Sử dụng loại giao diện thay vì địa chỉ để đảm bảo an toàn cho loại

Khi một hàm sử dụng địa chỉ hợp đồng làm đối số, tốt hơn nên chuyển giao diện hoặc loại hợp đồng hơn là địa chỉ thô. Nếu hàm được gọi ở nơi khác trong mã nguồn, trình biên dịch nó sẽ cung cấp các đảm bảo an toàn kiểu bổ sung.

Ở đây chúng tôi thấy hai lựa chọn thay thế:

contract Validator {function validate (uint) trả về bên ngoài (bool); } contract TypeSafeAuction {// hàm tốt validateBet (Validator _validator, uint _value) nội bộ trả về (bool) {bool valid = _validator.validate (_value); trở lại hợp lệ; }} contract TypeUnsafeAuction {// bad function validateBet (address _addr, uint _value) internal return (bool) {Validator validator = Validator (_addr); bool valid = validator.validate (_value); trở lại hợp lệ; }} Ngôn ngữ mã: JavaScript (javascript)

Các lợi ích của việc sử dụng hợp đồng TypeSafeAuction ở trên có thể được nhìn thấy từ ví dụ sau. Nếu validateBet () được gọi với một đối số địa chỉ hoặc một loại hợp đồng khác với Validator, trình biên dịch sẽ tạo ra lỗi này:

hợp đồng NonValidator {} hợp đồng Đấu giá là TypeSafeAuction {NonValidator nonValidator; function bet (uint _value) {bool valid = validateBet (nonValidator, _value); // TypeError: Kiểu đối số không hợp lệ trong lệnh gọi hàm. // Đã yêu cầu chuyển đổi ngầm định không hợp lệ từ NonValidator hợp đồng sang hợp đồng Validator. }} Ngôn ngữ mã: JavaScript (javascript)

Tránh sử dụng mã ngoại vi để kiểm tra các tài khoản thuộc sở hữu bên ngoài

Công cụ sửa đổi sau (hoặc một séc tương tự) thường được sử dụng để xác minh xem cuộc gọi được thực hiện từ tài khoản thuộc sở hữu bên ngoài (EOA) hay tài khoản hợp đồng:

// công cụ sửa đổi không hợp lệ isNotContract (address _a) {uint size; assembly {size: = extcodesize (_a)} request (size == 0); _; } Ngôn ngữ mã: JavaScript (javascript)

Ý tưởng rất dễ hiểu: nếu một địa chỉ chứa mã thì đó không phải là EOA mà là một tài khoản hợp đồng. Tuy nhiên, một hợp đồng không có sẵn mã nguồn trong quá trình xây dựng. Điều này có nghĩa là trong khi phương thức khởi tạo đang chạy, nó có thể thực hiện các cuộc gọi đến các hợp đồng khác, nhưng ngoại mã hóa địa chỉ của nó sẽ trả về số không. Dưới đây là một ví dụ tối thiểu cho thấy cách kiểm tra này có thể bị phá vỡ:

hợp đồng OnlyForEOA {uint public flag; // bad modifier isNotContract (address _a) {uint len; assembly {len: = extcodesize (_a)} request (len == 0); _; } function setFlag (uint i) public isNotContract (msg.sender) {flag = i; }} hợp đồng FakeEOA {constructor (address _a) public {OnlyForEOA c = OnlyForEOA (_a); c.setFlag (1); }} Ngôn ngữ mã: JavaScript (javascript)

Bởi vì địa chỉ hợp đồng có thể được tính toán trước, việc kiểm tra này cũng có thể không thành công nếu nó kiểm tra một địa chỉ trống ở khối n, nhưng có hợp đồng được triển khai cho nó ở một số khối lớn hơn n.

Cảnh báo: Vấn đề này có nhiều sắc thái. Nếu mục tiêu của bạn là ngăn các hợp đồng khác không thể gọi hợp đồng của bạn, thì kiểm tra ngoại mã có lẽ là đủ. Một cách tiếp cận khác là kiểm tra giá trị của (tx.origin == msg.sender), mặc dù điều này cũng có nhược điểm.

Có thể có các tình huống khác trong đó kiểm tra ngoại mã phục vụ mục đích của bạn. Mô tả tất cả chúng ở đây là ngoài phạm vi. Hiểu các hành vi cơ bản của EVM và sử dụng phán đoán của bạn.

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

Đặt lịch kiểm tra tại chỗ trong 1 ngày với các chuyên gia bảo mật của chúng tôi. Đặt trước cho bạn ngay hôm nay DiligenceSecuritySmart ContractsSolidityNewsletterĐăng ký nhận bản tin của chúng tôi để nhận tin tức Ethereum mới nhất, các 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:
map