Thực tiễn bảo mật hợp đồng thông minh Tact: Những lỗi phổ biến trong hệ sinh thái TON

avatar
Web3Caff
2 ngày trước
Bài viết này được dịch máy
Xem bản gốc

CertiK và TON cùng phát hành hướng dẫn mới nhất dành cho nhà phát triển hệ sinh thái TON , nhằm giúp các nhà phát triển tránh những lỗi và cạm bẫy phổ biến khi lập trình bằng ngôn ngữ Tact.

Tác giả: Certik

Ảnh bìa: Ảnh của ilgmyzin trên Bapt

TON(Mạng mở) tiếp tục mở rộng ranh giới của công nghệ blockchain với các tính năng cải tiến và hiệu suất hợp đồng thông minh mạnh mẽ. Dựa trên kinh nghiệm và bài học rút ra từ các nền tảng blockchain đời đầu (chẳng hạn như Ethereum, v.v.), TON cung cấp cho các nhà phát triển một hoàn cảnh phát triển linh hoạt và hiệu quả hơn. Trong đó trong những yếu tố chính thúc đẩy tiến trình này là ngôn ngữ lập trình Tact.

Tact là ngôn ngữ lập trình mới được thiết kế dành riêng cho Chuỗi TON , với mục tiêu cốt lõi là hiệu quả và đơn giản. Thật dễ dàng để học và sử dụng, đồng thời hoạt động hoàn hảo với các hợp đồng thông minh. Tact là một ngôn ngữ được gõ tĩnh với cú pháp đơn giản và hệ thống kiểu mạnh mẽ.

Tuy nhiên, nhiều vấn đề mà các nhà phát triển gặp phải khi sử dụng FunC vẫn tồn tại trong quá trình phát triển Tact. Sau đây sẽ phân tích một số lỗi phổ biến trong quá trình phát triển Tact dựa trên các trường hợp thực hành kiểm toán.

cấu trúc dữ liệu

Địa chỉ tùy chọn

Ngôn ngữ Tact đơn giản hóa việc khai báo, giải mã và mã hóa cấu trúc dữ liệu. Tuy nhiên, các nhà phát triển vẫn cần phải thận trọng. Hãy xem một ví dụ:

Đây là khai báo thông điệp InternalTransfer để truyền jetton theo tiêu chuẩn TEP-74 [1] . Vui lòng lưu ý khai báo reply_destination, là loại Địa chỉ. Trong Tact, địa chỉ được yêu cầu phải là địa chỉ khác 0. Tuy nhiên, việc triển khai tham khảo của tiêu chuẩn jetton [2] cho phép địa chỉ 0 (addr_none), được biểu thị bằng hai bit 0. Điều này có nghĩa là người dùng hoặc hợp đồng khác có thể cố gắng gửi một jetton có địa chỉ phản hồi bằng 0 và thao tác sẽ không thành công.

Ngoài ra, nếu tin nhắn Transfer do người dùng gửi về ví cho phép thiết lập reply_destination, nhưng tin nhắn InternalTransfer từ ví người gửi đến ví người nhận không hỗ trợ thông số này thì jetton sẽ “bay ra ngoài”, nghĩa là jetton không thể đến địa chỉ đích cuối cùng dẫn đến mất mát. Sau đó, chúng ta sẽ thảo luận về một ngoại lệ, cách xử lý thư bị trả lại đúng cách. Thông tin sẽ được xử lý đúng cách.

Trong trường hợp này, một khai báo có cấu trúc tốt hơn để cho phép địa chỉ 0 sẽ là Địa chỉ?, nhưng việc chuyển địa chỉ tùy chọn tới thư tiếp theo hiện đang rất phức tạp trong Tact.

Tuần tự hóa dữ liệu

Trong Tact, nhà phát triển có thể chỉ định cách các trường được tuần tự hóa.

Trong ví dụ này, TotalAmount sẽ được tuần tự hóa dưới dạng tiền xu và được phát hànhAmount sẽ được tuần tự hóa dưới dạng int257 (mặc định là Int). ReleaseAmount có thể âm và sẽ chiếm 257 bit. Trong hầu hết các trường hợp, việc bỏ qua kiểu tuần tự hóa không gây ra vấn đề gì; tuy nhiên, nếu dữ liệu liên quan đến giao tiếp thì điều này trở nên quan trọng.

Đây là một ví dụ từ một dự án chúng tôi kiểm toán:

Cấu trúc dữ liệu này được các dự án NFT sử dụng làm phản hồi cho các yêu cầu get_static_data [3] trên Chuỗi. Theo tiêu chuẩn, câu trả lời phải là:

Chỉ số trên là uint256 (không phải int257), nghĩa là dữ liệu trả về sẽ bị người gọi hiểu sai dẫn đến kết quả không thể đoán trước. Kết quả có thể xảy ra là trình xử lý report_static_data sẽ bị khôi phục và luồng thông báo sẽ bị gián đoạn. Những ví dụ này minh họa lý do tại sao việc xem xét tuần tự hóa dữ liệu lại quan trọng ngay cả khi sử dụng Tact. 

số nguyên có dấu

Việc không chỉ định loại tuần tự hóa cho Int có thể gây ra hậu quả nghiêm trọng hơn ví dụ trên. Không giống như tiền xu, int257 có thể âm, điều này thường khiến các lập trình viên ngạc nhiên. Ví dụ: trong hợp đồng thời gian thực của Tact, việc thấy số tiền: Int là cực kỳ phổ biến.

Bản thân phương pháp viết này không nhất thiết có nghĩa là có lỗ hổng, vì số tiền (số tiền) thường được mã hóa vào tin nhắn JettonTransfer hoặc được chuyển để gửi(SendParameters{value: money}), sử dụng loại tiền xu và không được phép giá trị âm. Tuy nhiên, trong một trường hợp, chúng tôi tìm thấy một hợp đồng có số dư lượng lớn cho phép người dùng đặt tất cả các giá trị thành số âm, bao gồm phần thưởng, phí, số tiền, giá cả, v.v. Do đó, những kẻ độc hại có thể khai thác lỗ hổng này.

đồng thời

Các nhà phát triển Chuỗi Ethereum phải nhận thức được các cuộc tấn công reentrancy, đó là khả năng gọi lại các chức năng của cùng một hợp đồng trước khi việc thực thi chức năng hiện tại hoàn tất. Trên Chuỗi TON , các cuộc tấn công tái diễn là không thể.

Vì TON là một hệ thống hỗ trợ các lệnh gọi hợp đồng thông minh song song và không đồng bộ nên việc theo dõi thứ tự các hành động xử lý có thể trở nên khó khăn hơn. Tài khoản mục tiêu sẽ nhận được mọi tin nhắn nội bộ và kết quả giao dịch sẽ được xử lý sau khi giao dịch diễn ra, nhưng không có đảm bảo nào khác (xem tài liệu liên quan [4] để biết thêm thông tin về truyền thông điệp).

Chúng tôi không thể dự đoán liệu tin nhắn 3 hay tin nhắn 4 sẽ được gửi trước.

Trong trường hợp này, Tấn công trung gian [5] là kiểu tấn công phổ biến trong luồng tin nhắn. Để đảm bảo tính bảo mật, nhà phát triển nên đặt thời gian gửi của mỗi tin nhắn từ 1 đến 100 giây, trong thời gian đó mọi tin nhắn khác đều có thể được gửi. Dưới đây là một số cân nhắc bổ sung có thể cải thiện bảo mật:

1. Không kiểm tra hoặc cập nhật trạng thái hợp đồng cho các bước tiếp theo trong luồng tin nhắn.

2. Sử dụng mẫu giá trị mang [6] . Không gửi thông tin về giá trị trực tiếp mà bằng tin nhắn.

Đây là một ví dụ thực tế về một lỗ hổng:

Trong ví dụ trên, các bước sau xảy ra:

1. Người dùng gửi jetton đến NftCollection thông qua Collection_jetton_wallet.

2. TransferNotification được gửi đến hợp đồng NftCollection và hợp đồng ghi lại đã nhận_jetton_amount.

3. Hợp đồng chuyển tiếp jetton cho chủ sở hữu NftCollection.

4. Gửi tin nhắn Quá mức tới NftCollection dưới dạng reply_destination.

5. NftItem được triển khai trong trình xử lý Vượt quá, sử dụng đã nhận_jetton_amount.

Có một số vấn đề cần lưu ý ở đây:

Đầu tiên , tin nhắn vượt mức không được đảm bảo sẽ được gửi theo tiêu chuẩn jetton. Nếu không đủ gas để gửi tin nhắn Dư thừa, tin nhắn này sẽ bị bỏ qua và luồng tin nhắn sẽ dừng lại.

Thứ hai , việc cập nhật đã nhận_jetton_amount và sau đó sử dụng nó khiến hệ thống dễ bị thực thi đồng thời. Những người dùng khác có thể đồng thời gửi một số tiền khác và ghi đè lên số tiền đã lưu, số tiền này cũng có thể bị khai thác một cách ác ý để kiếm lợi từ số tiền đó.

Trong trường hợp đồng thời, TON tương tự như các hệ thống đa luồng tập trung truyền thống.

Xử lý tin nhắn bị trả lại

Nhiều hợp đồng bỏ qua việc xử lý các tin nhắn bị trả lại. Tuy nhiên, Tact làm cho quá trình này trở nên đơn giản và dễ hiểu:

Để quyết định xem có nên gửi tin nhắn ở chế độ có thể gửi lại hay không, có thể xem xét hai yếu tố:

1. Nếu tin nhắn không thành công, ai sẽ nhận thêm Toncoin? Nếu mục tiêu nhận được số tiền này thay vì gửi hợp đồng thì hãy gửi tin nhắn ở chế độ không thể hoàn lại [7] .

2. Điều gì xảy ra với luồng tin nhắn nếu tin nhắn tiếp theo bị từ chối? Nếu có thể khôi phục trạng thái nhất quán bằng cách xử lý thư trả về thì tốt nhất nên làm như vậy. Nếu không thể khôi phục được thì tốt nhất nên sửa đổi luồng tin nhắn.

Sau đây là một ví dụ từ tiêu chuẩn jetton [8] :

1. Thông báo Vượt quá được gửi ở chế độ không thể hoàn trả vì hợp đồng không cần hoàn trả toncoin.

2. Gửi tin nhắn TransferNotification ở chế độ không thể trả lại, vì Forward_ton_amount thuộc về người gọi và hợp đồng sẽ không giữ lại nó.

3. Ngược lại, BurnNotification được gửi ở chế độ có thể trả lại vì nếu nó được trả lại theo hợp đồng chính jetton, ví cần khôi phục số dư của nó để giữ cho tổng_supply nhất quán. 4. Chuyển khoản nội bộ cũng có thể được hoàn lại. Nếu người nhận từ chối tiền, ví của người gửi phải cập nhật số dư. Hãy ghi nhớ những điểm sau:

1. Tin nhắn bị trả lại chỉ nhận được 256 bit [9] của tin nhắn gốc; sau khi nhận dạng tin nhắn, chỉ còn 224 bit dữ liệu hợp lệ. Do đó, bạn sẽ nhận được thông tin hạn chế về hoạt động thất bại, thường là một số tiền được lưu trữ dưới dạng tiền xu.

2. Nếu không đủ phí gas , tin nhắn trả lại sẽ không được gửi. 3. Bản thân tin nhắn bị trả lại không thể bị trả lại lần nữa. 

Trở về Jetton

Trong một số trường hợp, việc hoàn tác và xử lý tin nhắn bị trả lại không phải là một lựa chọn. Ví dụ phổ biến nhất là khi hợp đồng của bạn nhận được TransferNotification về một jetton đang đến, việc trả lại tin nhắn đó có thể khiến jetton bị khóa vĩnh viễn. Thay vào đó, bạn nên sử dụng khối try-catch [10] .

Hãy xem một ví dụ. Trong EVM, khi một giao dịch bị đảo ngược, tất cả kết quả sẽ được khôi phục (ngoại trừ gas- do thợ đào khai thác tính phí). Nhưng trong TVM, "giao dịch" bị phân tách thành sê-ri tin nhắn, do đó, việc khôi phục chỉ một trong đó các tin nhắn có thể khiến trạng thái "nhóm hợp đồng" không nhất quán.

Để giải quyết vấn đề này, tất cả các điều kiện phải được kiểm tra theo cách thủ công và các thông báo khắc phục được gửi qua lại trong trường hợp khẩn cấp. Tuy nhiên, vì việc phân tích cú pháp tải trọng mà không có ngoại lệ là rất tẻ nhạt nên tốt hơn nên sử dụng khối try-catch.

Sau đây là ví dụ về mã nhận Jetton điển hình:

Lưu ý rằng ngay cả việc gửi jetton trở lại cũng sẽ không hoạt động bình thường nếu phí gas không đủ. Ngoài ra, điều quan trọng cần lưu ý là chúng tôi đang trả lại jetton thông qua "ví" của người gửi(), chứ không phải thông qua ví jetton thực tế trong hợp đồng của chúng tôi. Điều này là do bất kỳ ai cũng có thể lừa chúng tôi bằng cách gửi tin nhắn TransferNotification theo cách thủ công.

Quản lý phí gas

Khi kiểm toán hợp đồng TON , một trong những vấn đề phổ biến nhất là quản lý phí gas . Có hai lý do chính:

1. Thiếu kiểm soát phí gas có thể dẫn đến các vấn đề sau :

Quá trình thực thi luồng thông báo chưa hoàn tất: một số thao tác sẽ có hiệu lực, trong khi một số thao tác khác sẽ bị hủy do không đủ gas . Ví dụ: nếu hoạt động mua phần thưởng được hoàn thành trong ví jetton, nhưng hoạt động đốt thị phần bị bỏ qua trong hợp đồng chính của jetton thì toàn bộ nhóm hợp đồng sẽ trở nên không nhất quán.

Người dùng có thể rút số dư hợp đồng của mình : Ngoài ra, Toncoin quá mức có thể tích lũy trong hợp đồng. 2. Các nhà phát triển hợp đồng TON gặp khó khăn trong việc quản lý và kiểm soát gas: Các nhà phát triển khéo léo cần đạt được mức tiêu thụ gas thông qua thử nghiệm và cập nhật các giá trị tương ứng lần khi luồng thông báo được cập nhật trong quá trình phát triển.

Đây là những gì chúng tôi khuyên bạn nên:

1. Xác định "điểm vào": đây là tất cả các bộ xử lý tin nhắn có thể chấp nhận tin nhắn từ "bên ngoài", tức là từ người dùng cuối hoặc các hợp đồng khác (chẳng hạn như ví Jetton).

2. Đối với mỗi điểm vào, hãy vẽ tất cả các đường đi có thể và tính toán mức tiêu thụ gas . Sử dụng printTransactionFees() (có trong @ TON/sandbox, đi kèm với Blueprint [11] ).

3. Nếu hợp đồng có thể được triển khai trong luồng tin nhắn, thì giả định rằng hợp đồng đó sẽ được triển khai. Việc triển khai sẽ tiêu tốn nhiều gas và phí lưu trữ hơn. 4. Tại mỗi điểm vào, hãy thêm yêu cầu gas tối thiểu tùy theo tình hình.

5. Nếu bộ xử lý không gửi thêm tin nhắn (luồng tin nhắn kết thúc ở đây), thì tốt hơn là trả về phần Vượt quá, như hiển thị bên dưới:

Có thể không gửi Số dư, nhưng đối với các hợp đồng có thông lượng cao như Jetton Master, hợp đồng có lượng lớn tin nhắn BurnNotification hoặc lượng lớn các lần chuyển đến, số tiền tích lũy có thể tăng trưởng nhanh.

6. Nếu bộ xử lý chỉ gửi một tin nhắn - bao gồm cả emitter(), đây thực sự là một tin nhắn bên ngoài - thì cách đơn giản nhất là chuyển lượng gas còn lại qua Forward() (xem ở trên).

7. Nếu bộ xử lý gửi nhiều tin nhắn hoặc nếu có TON tham gia vào quá trình liên lạc, thì việc tính toán số lượng cần gửi sẽ dễ dàng hơn số lượng cần gửi còn lại.

Trong ví dụ tiếp theo, giả sử hợp đồng muốn gửi số tiền chuyển tiếp cho hai hợp đồng phụ dưới dạng tiền đặt cọc:

Như bạn có thể thấy, việc quản lý phí gas đòi hỏi rất nhiều sự chú ý, ngay cả trong những trường hợp đơn giản. Lưu ý rằng bạn không thể sử dụng cờ SendRemainingValue trong chế độ send() nếu bạn đã gửi tin nhắn, trừ khi bạn cố tình muốn tiêu tiền từ số dư hợp đồng .

Tóm lại

Khi hệ sinh thái TON phát triển, việc phát triển an toàn các hợp đồng thông minh Tact sẽ ngày càng trở nên quan trọng. Mặc dù Tact mang lại hiệu quả cao hơn và đơn giản hơn nhưng các nhà phát triển vẫn phải thận trọng để tránh những cạm bẫy phổ biến. Bằng cách hiểu những lỗi phổ biến và áp dụng các phương pháp hay nhất, các nhà phát triển có thể khai thác toàn bộ tiềm năng của Tact và tạo ra các hợp đồng thông minh mạnh mẽ và an toàn. Việc học hỏi liên tục và tuân theo các nguyên tắc thực hành bảo mật sẽ đảm bảo rằng các khả năng đổi mới của hệ sinh thái TON được sử dụng một cách an toàn và hiệu quả, từ đó góp phần tạo nên một hoàn cảnh blockchain an toàn và đáng tin cậy hơn.

[1] TEP-74: https://github.com/ TON-blockchain/TEPs/blob/master/text/0074-jettons-standard.md#1-transfer[2] Triển tham khảo: https://github. com/ TON-blockchain/token-contract/[3] get_static_data: https://github.com/ TON-blockchain/TEPs/blob/master/text/0062-nft-standard.md#2-get_static_data[4] Tài liệu liên quan: https:// TON. đảm bảo#message-delivery[5] Tấn công trung gian: https:// TON. .org/develop/smart-contracts/security/secure-programming#3-expect-a-man-in-the-middle-of-the-message-flow[6] Chế độ mang giá trị: https:// TON. .org/develop/smart-contracts/security/secure-programming#4-use-a-carry-value-pattern[7] Mẫu không dự phòng: https:// TON. .org/develop/smart-contracts/guidelines/non-bouncable-messages[8] tiêu chuẩn jetton: https://github.com/ton -blockchain/TEPs/blob / master/text/0074-jettons-standard.md# 1-transfer[9] chỉ chấp nhận 256 bit: https://docs.tact-lang.org/book/bounced/#caveats[10] try-catch Chặn: https://docs.tact-lang.org/book/statements#try-catch[11] Bản thiết kế: TON

Tuyên bố miễn trừ trách nhiệm: Là một nền tảng thông tin blockchain, các bài viết được xuất bản trên trang này chỉ thể hiện quan điểm tác giả và khách và không liên quan gì đến quan điểm của Web3Caff. Thông tin trong bài viết chỉ tham khảo và không cấu thành bất kỳ lời khuyên hay đề nghị đầu tư nào. Vui lòng tuân thủ luật pháp và quy định có liên quan của quốc gia hoặc khu vực nơi bạn sinh sống.

Chào mừng bạn tham gia cộng đồng Web3Caff chính thức : Tài khoản X (Twitter) | Nhóm đọc WeChat | Nhóm đăng ký Telegram |

Nguồn
Tuyên bố từ chối trách nhiệm: Nội dung trên chỉ là ý kiến của tác giả, không đại diện cho bất kỳ lập trường nào của Followin, không nhằm mục đích và sẽ không được hiểu hay hiểu là lời khuyên đầu tư từ Followin.
Thích
Thêm vào Yêu thích
Bình luận