Mô hình bảo mật hợp đồng DeFi mới: Tập trung vào tính bất biến của giao thức

Bản tóm tắt

Đừng chỉ viết các câu lệnh yêu cầu cho các chức năng cụ thể; hãy viết các câu lệnh yêu cầu cho các giao thức của bạn. Kiểm tra tuân thủ chức năng (yêu cầu)-hiệu quả (Hiệu ứng)-tương tác (INinteractions) + bất biến giao thức (Iniants) hoặc chế độ FREI-PI có thể giúp hợp đồng của bạn an toàn hơn, vì nó buộc các nhà phát triển phải tập trung vào bảo mật cấp chức năng. Ngoài ra, hãy chú ý cho các bất biến ở cấp độ giao thức.

động lực

Vào tháng 3 năm 2023, Euler Finance bị hack và mất 200 triệu USD. Euler Finance là một thị trường cho vay nơi người dùng có thể ký gửi tài sản thế chấp và vay dựa vào đó. Nó có một số tính năng độc đáo, trên thực tế, chúng là một thị trường cho vay có thể so sánh với Compound Finance và Aave.

Bạn có thể đọc khám nghiệm tử thi về vụ hack này tại đây. Nội dung chính của nó là thiếu kiểm tra sức khỏe trong một chức năng cụ thể, cho phép người dùng phá vỡ tính bất biến cơ bản của thị trường cho vay.

Người mới bắt đầu cơ bản

Cốt lõi của hầu hết các giao thức DeFi là tính bất biến, một thuộc tính của trạng thái chương trình được cho là luôn đúng. Cũng có thể có nhiều bất biến, nhưng nhìn chung, chúng được xây dựng xung quanh một ý tưởng cốt lõi. Dưới đây là một số ví dụ:

  • Như trong thị trường cho vay: người dùng không thể thực hiện bất kỳ hành động nào để đặt bất kỳ tài khoản nào vào vị trí tài sản thế chấp không an toàn hoặc kém an toàn hơn ("kém an toàn" có nghĩa là nó đã ở dưới ngưỡng an toàn tối thiểu và do đó không thể rút thêm được).
  • Trong AMM DEX: x * y == k, x + y == k, v.v.
  • Trong đặt cược khai thác thanh khoản: người dùng chỉ có thể rút số lượng mã thông báo đặt cược mà họ đã gửi.

Điều không ổn với Euler Finance không nhất thiết là họ đã thêm các tính năng, không viết bài kiểm tra hoặc không tuân theo các phương pháp hay nhất truyền thống. Họ đã kiểm tra bản nâng cấp và có các thử nghiệm, nhưng nó vẫn trượt qua các vết nứt. Vấn đề cốt lõi là họ quên đi một bất biến cốt lõi của thị trường cho vay (các kiểm toán viên cũng vậy!).

*Lưu ý: Tôi không cố gắng chọn Euler, họ là một đội tài năng, nhưng đây là trường hợp gần đây. *

Cốt lõi của vấn đề

Có lẽ bạn đang nghĩ "Chà, đúng vậy. Đó là lý do tại sao họ bị hack; họ quên mất câu lệnh yêu cầu". Có và không.

Nhưng tại sao họ lại quên câu lệnh yêu cầu?

kiểm tra - xác nhận - tương tác chưa tốt

Một mẫu phổ biến được đề xuất cho các nhà phát triển solidity là mẫu Kiểm tra-Hiệu ứng-Tương tác. Nó rất hữu ích để loại bỏ các lỗi liên quan đến việc đăng nhập lại và thường làm tăng số lượng xác thực đầu vào mà nhà phát triển phải thực hiện. _Nhưng_, dễ xảy ra tình trạng nhìn cây không thấy rừng.

Những gì nó dạy cho nhà phát triển là: "Đầu tiên tôi viết câu lệnh yêu cầu của mình, sau đó tôi thực hiện xác thực, sau đó có thể tôi thực hiện bất kỳ tương tác nào, sau đó tôi an toàn". Vấn đề là, thường xuyên hơn không, nó trở thành một hỗn hợp giữa kiểm tra và hiệu ứng -- không tệ, phải không? Các tương tác vẫn là cuối cùng, vì vậy việc vào lại không phải là vấn đề. Nhưng nó buộc người dùng phải tập trung vào các chức năng cụ thể hơn và các chuyển đổi trạng thái riêng lẻ hơn là bối cảnh toàn cầu, rộng lớn hơn. Điều này có nghĩa rằng:

Mẫu tương tác kiểm tra-xác thực-tương tác đơn thuần khiến các nhà phát triển quên đi các bất biến cốt lõi trong các giao thức của họ.

Đây vẫn là một mẫu tuyệt vời cho các nhà phát triển, nhưng tính bất biến của giao thức phải luôn được đảm bảo (nghiêm túc mà nói, bạn vẫn nên sử dụng CEI!).

Làm đúng: Chế độ FREI-PI

Lấy ví dụ đoạn trích này từ hợp đồng SoloMargin của dYdX (mã nguồn), là một thị trường cho vay và hợp đồng giao dịch có đòn bẩy. Đây là một ví dụ điển hình về cái mà tôi gọi là mẫu Yêu cầu chức năng-Hiệu ứng-Tương tác + Khởi tạo giao thức hoặc mẫu FREI-PI.

Như vậy, tôi tin rằng đây là thị trường cho vay duy nhất trong thị trường cho vay giai đoạn đầu không có bất kỳ lỗ hổng nào liên quan đến thị trường. Hợp chất và Aave không có vấn đề trực tiếp, nhưng các nhánh của chúng thì có. Và bZx đã bị hack nhiều lần.

Kiểm tra đoạn mã dưới đây và chú ý những điều trừu tượng sau:

  1. Kiểm tra tham số đầu vào (_verifyInputs).
  2. Hành động (chuyển đổi dữ liệu, thao tác trạng thái)
  3. Kiểm tra trạng thái cuối cùng (_verifyFinalState).

Các Kiểm tra-Hiệu ứng-Tương tác thông thường vẫn được thực hiện. Điều đáng chú ý là tương tác xác thực-kiểm tra với các kiểm tra bổ sung không tương đương với FREI-PI--chúng giống nhau nhưng phục vụ các mục tiêu khác nhau về cơ bản. Do đó, các nhà phát triển nên nghĩ về chúng theo các cách khác nhau: FREI-PI, với tính trừu tượng cao hơn, nhằm mục đích an toàn giao thức, trong khi CEI nhắm đến an toàn chức năng.

Cấu trúc của hợp đồng này thực sự thú vị - người dùng có thể thực hiện các hành động họ muốn (gửi, vay, giao dịch, chuyển nhượng, thanh lý, v.v.) trong một chuỗi hành động. Bạn muốn gửi 3 mã thông báo khác nhau, rút tiền thứ 4 và thanh lý tài khoản? Đó là một cuộc gọi duy nhất.

Đây là sức mạnh của FREI-PI: người dùng có thể làm bất cứ điều gì họ muốn trong giao thức, miễn là các yếu tố bất biến của thị trường cho vay cốt lõi giữ nguyên ở cuối cuộc gọi: một người dùng không thể thực hiện bất kỳ hành động nào khiến bất kỳ tài khoản nào không an toàn hoặc nhiều hơn Các vị trí thế chấp không an toàn. Đối với hợp đồng này, điều này được thực hiện trong _verifyFinalState , kiểm tra tài sản thế chấp của từng tài khoản bị ảnh hưởng để đảm bảo rằng thỏa thuận tốt hơn so với khi giao dịch được bắt đầu.

Có một số bất biến bổ sung được bao gồm trong chức năng này bổ sung cho các bất biến cốt lõi và trợ giúp với các chức năng phụ như đóng cửa thị trường, nhưng chính các kiểm tra cốt lõi mới thực sự giữ an toàn cho giao thức.

FREI-PI lấy thực thể làm trung tâm

Một vấn đề khác với FREI-PI là khái niệm lấy thực thể làm trung tâm. Lấy một thị trường cho vay và các bất biến cốt lõi giả định làm ví dụ:

Về mặt kỹ thuật, đây không phải là bất biến duy nhất, nhưng nó dành cho thực thể người dùng (nó vẫn là bất biến giao thức cốt lõi và thông thường, bất biến người dùng là bất biến giao thức cốt lõi). Thị trường cho vay cũng thường có 2 thực thể bổ sung:

  • Tiên tri
  • Quản lý / Quản trị

Mỗi tính bất biến bổ sung làm cho giao thức trở nên khó bảo đảm hơn, vì vậy càng ít càng tốt. Đây thực sự là những gì Dan Elitzer đã nói trong bài viết của anh ấy có tiêu đề: Tại sao DeFi bị hỏng và cách khắc phục nó #1. Giao thức không có tiên tri (gợi ý: bài báo không thực sự nói rằng các tiên tri là vấn đề).

Nhà tiên tri

Đối với các nhà tiên tri, hãy khai thác Cream Finance trị giá 130 triệu đô la. Tính bất biến cốt lõi của các thực thể tiên tri:

Hóa ra, việc xác minh lời tiên tri trong thời gian chạy bằng FREI-PI là một công việc phức tạp, nhưng có thể thực hiện được với một số suy tính trước. Nói chung, Chainlink là một lựa chọn tốt để dựa vào phần lớn, đáp ứng hầu hết tính bất biến. Trong trường hợp thao túng hoặc bất ngờ hiếm gặp, có thể có lợi khi có các biện pháp bảo vệ làm giảm tính linh hoạt để tăng độ chính xác (chẳng hạn như kiểm tra xem giá trị đã biết cuối cùng có lớn hơn giá trị hiện tại vài phần trăm hay không). Ngoài ra, hệ thống SoloMargin của dYdX hoạt động rất tốt với lời tiên tri DAI của họ, đây là mã (nếu bạn không thể biết, tôi nghĩ đó là hệ thống hợp đồng thông minh phức tạp tốt nhất từng được viết).

Để biết thêm về đánh giá tiên tri và làm nổi bật khả năng của nhóm Euler, họ đã viết một bài viết hay về thao tác tính toán giá tiên tri Uniswap V3 TWAP.

Quản trị / Quản trị

Tạo bất biến cho các thực thể được quản lý là khó nhất. Điều này chủ yếu là do hầu hết vai trò của chúng là thay đổi các bất biến hiện có khác. Điều đó nói rằng, nếu bạn có thể tránh sử dụng vai trò quản trị, bạn nên làm.

Về cơ bản, các bất biến cốt lõi của một thực thể được quản lý có thể là:

Diễn giải: Quản trị viên có thể làm những việc mà cuối cùng sẽ không phá vỡ các bất biến, trừ khi họ thay đổi mạnh mẽ mọi thứ để bảo vệ tiền của người dùng (ví dụ: chuyển tài sản vào hợp đồng giải cứu là loại bỏ các bất biến). Quản trị viên cũng nên được coi là một người dùng, do đó, tính bất biến của người dùng trong thị trường cho vay cốt lõi cũng phải phù hợp với họ (có nghĩa là họ không thể tấn công người dùng hoặc giao thức khác). Hiện tại, một số hành động của quản trị viên không thể xác minh trong thời gian chạy thông qua FREI-PI, nhưng với các bất biến đủ mạnh ở những nơi khác, hy vọng rằng hầu hết các sự cố có thể được giảm thiểu. Tôi nói hiện tại, bởi vì người ta có thể tưởng tượng việc sử dụng hệ thống bằng chứng zk có thể kiểm tra toàn bộ trạng thái của hợp đồng (mỗi người dùng, mỗi nhà tiên tri, v.v.).

Để làm ví dụ về việc quản trị viên phá vỡ tính bất biến, hãy thực hiện hành động quản trị Hợp chất đã phá vỡ thị trường cETH vào tháng 8 năm 2022. Về cơ bản, bản nâng cấp này phá vỡ tính bất biến của Oracle: Oracle cung cấp thông tin chính xác và (tương đối) theo thời gian thực. Do thiếu chức năng, Oracle có thể cung cấp thông tin không chính xác. Xác minh FREI-PI trong thời gian chạy, kiểm tra xem Oracle bị ảnh hưởng có thể cung cấp thông tin theo thời gian thực hay không, có thể ngăn điều này xảy ra với bản nâng cấp. Điều này có thể được tích hợp vào _setPriceOracle để kiểm tra xem tất cả nội dung có nhận được thông tin theo thời gian thực hay không. Điều thú vị về FREI-PI dành cho vai trò quản trị viên là vai trò quản trị viên tương đối không nhạy cảm với giá cả (hoặc ít nhất là chúng nên như vậy), vì vậy việc sử dụng nhiều gas hơn không phải là vấn đề lớn.

Phức tạp là nguy hiểm

Vì vậy, trong khi các bất biến quan trọng nhất là các bất biến cốt lõi của giao thức, thì cũng có thể có một số bất biến tập trung vào thực thể mà các bất biến cốt lõi phải nắm giữ. Tuy nhiên, tập bất biến đơn giản nhất (và nhỏ nhất) có lẽ là an toàn nhất. Đơn giản là tốt Một ví dụ điển hình là Uniswap…

Tại sao Uniswap chưa bao giờ bị hack (chắc vậy)

AMM có thể có bất biến cơ bản đơn giản nhất so với bất kỳ nguyên mẫu DeFi nào: tokenBalanceX * tokenBalanceY == k (ví dụ: mô hình sản phẩm không đổi). Mọi chức năng trong Uniswap V2 đều xoay quanh bất biến đơn giản này:

  1. Bạc hà: thêm vào k
  2. Burn: trừ k
  3. Hoán đổi: chuyển x và y, nhưng giữ nguyên k.
  4. Skim: điều chỉnh lại tokenBalanceX * tokenBalanceY để làm cho nó bằng k và loại bỏ phần thừa.

Bí mật bảo mật của Uniswap V2: cốt lõi là một tính bất biến đơn giản và tất cả các chức năng đều phục vụ cho nó. Thực thể duy nhất khác có thể được tranh luận là quản trị, có thể bật công tắc phí, điều này không ảnh hưởng đến tính bất biến cốt lõi, chỉ là phân phối quyền sở hữu số dư mã thông báo. Sự đơn giản trong tuyên bố bảo mật của họ là lý do tại sao Uniswap chưa bao giờ bị hack. Sự đơn giản thực ra không phải là sự coi thường các nhà phát triển xuất sắc của các hợp đồng thông minh của Uniswap, mà ngược lại, các kỹ sư xuất sắc cần phải tìm ra sự đơn giản.

Vấn đề khí đốt

Twitter của tôi đã đầy rẫy những tiếng kêu kinh hoàng và đau đớn của những người theo chủ nghĩa tối ưu hóa rằng những kiểm tra này là không cần thiết và không hiệu quả. Hai điều về câu hỏi này:

  1. Bạn có biết điều gì khác không hiệu quả không? Phải gửi tin nhắn tới ~~Laurence~~ tin tặc Bắc Triều Tiên thông qua etherscan, chuyển tiền bằng ETH và đe dọa rằng FBI sẽ can thiệp.
  2. Có thể bạn đã tải tất cả dữ liệu mình cần từ bộ lưu trữ, vì vậy khi kết thúc cuộc gọi, chỉ cần thêm một chút kiểm tra yêu cầu vào dữ liệu nóng. Bạn có muốn thỏa thuận của mình chỉ tốn một khoản phí không đáng kể hay để nó chết?

Nếu chi phí quá cao, hãy suy nghĩ lại về các yếu tố cốt lõi và cố gắng đơn giản hóa.

Điều này có ý nghĩa gì với tôi?

Là một nhà phát triển, điều quan trọng là sớm xác định và thể hiện các bất biến cốt lõi trong quá trình phát triển. Như một gợi ý cụ thể: chức năng đầu tiên bạn cần viết là _verifyAfter, để xác minh các bất biến của bạn sau mỗi lần gọi đến hợp đồng của bạn. Đặt nó trong hợp đồng của bạn và triển khai nó ở đó. Bổ sung bất biến này (và các bất biến tập trung vào thực thể khác) bằng các thử nghiệm bất biến rộng hơn được kiểm tra trước khi triển khai (Hướng dẫn về Foundry).

Cửa hàng tạm thời mở ra một số cải tiến và tối ưu hóa thú vị mà Nascent sẽ thử nghiệm -- Tôi khuyên bạn nên cân nhắc cách sử dụng cửa hàng tạm thời như một công cụ để đảm bảo an toàn tốt hơn trong các ngữ cảnh cuộc gọi.

Trong bài viết này, chúng tôi không dành nhiều thời gian cho việc giới thiệu mô hình FREI-PI để xác thực đầu vào, nhưng nó cũng rất quan trọng. Xác định giới hạn của đầu vào là một nhiệm vụ khó khăn để tránh tràn và các tình huống tương tự. Cân nhắc kiểm tra và theo dõi tiến trình của công cụ của chúng tôi: hỏa kế (hiện đang ở giai đoạn thử nghiệm, vui lòng cho chúng tôi một ngôi sao). Nó có thể đi sâu và giúp tìm những nơi mà bạn có thể không thực hiện xác thực đầu vào.

Tóm lại là

Ngoài bất kỳ từ viết tắt hấp dẫn nào (FREI-PI) hoặc tên lược đồ, phần thực sự quan trọng là:

Tìm sự đơn giản trong tính bất biến cốt lõi của giao thức của bạn. Và làm việc cật lực để đảm bảo rằng nó không bao giờ bị phá hủy (hoặc bị bắt trước khi nó xảy ra).

Xem bản gốc
This page may contain third-party content, which is provided for information purposes only (not representations/warranties) and should not be considered as an endorsement of its views by Gate, nor as financial or professional advice. See Disclaimer for details.
  • Phần thưởng
  • Bình luận
  • Chia sẻ
Bình luận
0/400
Không có bình luận
  • Ghim
Giao dịch tiền điện tử mọi lúc mọi nơi
qrCode
Quét để tải xuống ứng dụng Gate
Cộng đồng
Tiếng Việt
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)