Phân tích chi tiết lỗ hổng biên dịch viên Solidity và chiến lược ứng phó
Trình biên dịch là một trong những thành phần cơ bản của hệ thống máy tính hiện đại. Nó là một chương trình máy tính, chức năng chính là chuyển đổi mã nguồn của ngôn ngữ lập trình bậc cao thành mã lệnh có thể thực thi bởi CPU hoặc máy ảo của máy tính.
Mặc dù hầu hết các nhà phát triển và nhân viên an ninh thường chú trọng vào tính an toàn của mã ứng dụng, nhưng tính an toàn của chính trình biên dịch cũng quan trọng không kém. Là một chương trình máy tính, trình biên dịch cũng có thể tồn tại lỗ hổng bảo mật, trong một số trường hợp có thể gây ra rủi ro an ninh nghiêm trọng. Ví dụ, khi trình duyệt biên dịch và phân tích mã JavaScript phía trước, có thể do lỗ hổng của engine phân tích JavaScript mà người dùng khi truy cập vào trang web độc hại có thể bị kẻ tấn công lợi dụng, cuối cùng dẫn đến việc kiểm soát trình duyệt của nạn nhân thậm chí là hệ điều hành.
Trình biên dịch Solidity cũng tồn tại lỗ hổng bảo mật. Theo cảnh báo an toàn của nhóm phát triển Solidity, đã phát hiện ra vấn đề bảo mật trong nhiều phiên bản khác nhau của trình biên dịch Solidity.
Lỗ hổng trong trình biên dịch Solidity
Chức năng chính của trình biên dịch Solidity là chuyển đổi mã hợp đồng thông minh thành mã lệnh (EVM) cho máy ảo Ethereum. Những mã lệnh EVM này được đóng gói trong giao dịch và tải lên Ethereum, cuối cùng được EVM phân tích và thực thi.
Cần lưu ý rằng lỗ hổng trình biên dịch Solidity khác với lỗ hổng EVM. Lỗ hổng EVM đề cập đến các vấn đề bảo mật phát sinh khi máy ảo thực hiện các lệnh. Do kẻ tấn công có thể tải lên bất kỳ mã nào vào Ethereum, nếu EVM có lỗ hổng bảo mật, điều đó sẽ ảnh hưởng đến toàn bộ mạng Ethereum, có thể dẫn đến từ chối dịch vụ (DoS) thậm chí toàn bộ blockchain bị kẻ tấn công chiếm đoạt. Tuy nhiên, thiết kế EVM tương đối đơn giản, mã lõi được cập nhật không thường xuyên, vì vậy khả năng xảy ra các vấn đề như vậy là tương đối thấp.
Lỗi biên dịch Solidity đề cập đến các vấn đề xảy ra khi trình biên dịch chuyển đổi mã Solidity thành mã EVM. Khác với việc trình duyệt biên dịch và chạy JavaScript trên máy khách của người dùng, quá trình biên dịch Solidity chỉ diễn ra trên máy tính của nhà phát triển hợp đồng thông minh và không được thực thi trên Ethereum. Do đó, lỗi biên dịch Solidity sẽ không ảnh hưởng trực tiếp đến mạng Ethereum.
Một mối nguy hiểm chính của lỗ hổng trình biên dịch Solidity là có thể dẫn đến mã EVM được tạo ra không nhất quán với mong đợi của nhà phát triển. Vì các hợp đồng thông minh trên Ethereum thường liên quan đến tài sản tiền điện tử của người dùng, bất kỳ lỗi hợp đồng nào do trình biên dịch gây ra cũng có thể dẫn đến mất mát tài sản của người dùng, với hậu quả nghiêm trọng.
Các nhà phát triển và các kiểm toán viên hợp đồng có thể chủ yếu chú ý đến các vấn đề thực hiện logic mã hợp đồng, cũng như các vấn đề bảo mật cấp Solidity như tấn công tái nhập và tràn số nguyên. Tuy nhiên, lỗ hổng biên dịch thường khó phát hiện chỉ bằng cách kiểm toán mã nguồn hợp đồng. Cần phải phân tích kết hợp giữa phiên bản trình biên dịch cụ thể và các mẫu mã cụ thể để xác định xem hợp đồng thông minh có bị ảnh hưởng bởi lỗ hổng biên dịch hay không.
Ví dụ về lỗ hổng trình biên dịch Solidity
Dưới đây là một vài ví dụ thực tế về lỗ hổng biên dịch Solidity, cho thấy hình thức cụ thể, nguyên nhân và tác hại của chúng.
SOL-2016-9 HighOrderByteCleanStorage
Lỗ hổng này tồn tại trong các phiên bản biên dịch Solidity sớm hơn (>=0.1.6 <0.4.4).
Xem xét mã sau:
solidity
hợp đồng C {
uint32 a = 0x12345678;
uint32 b = 0;
function f() public {
a = a + 1;
}
function run() public view returns (uint) {
return b;
}
}
Biến lưu trữ b không bị thay đổi, vì vậy hàm run() nên trả về giá trị mặc định 0. Tuy nhiên, trong mã được tạo ra bởi trình biên dịch phiên bản có lỗ hổng, run() sẽ trả về 1.
Trong trường hợp không hiểu rõ lỗ hổng của trình biên dịch, các nhà phát triển thông thường rất khó phát hiện lỗi này chỉ bằng việc kiểm tra mã đơn giản. Mặc dù ví dụ này tương đối đơn giản và không gây ra hậu quả nghiêm trọng, nhưng nếu biến b được sử dụng cho xác thực quyền, ghi chép tài sản và các mục đích khác, sự không nhất quán này có thể dẫn đến hậu quả rất nghiêm trọng.
Nguyên nhân gây ra hiện tượng này là do EVM sử dụng máy ảo theo kiểu ngăn xếp, mỗi phần tử trong ngăn xếp đều có kích thước 32 byte ( tức là kích thước của biến uint256 ). Mặt khác, mỗi slot trong bộ nhớ lưu trữ (storage) cũng có kích thước 32 byte. Ngôn ngữ Solidity hỗ trợ các kiểu dữ liệu có kích thước nhỏ hơn 32 byte như uint32, và khi biên dịch, trình biên dịch cần thực hiện các thao tác xóa thích hợp trên các bit cao hơn ( clean up ) để đảm bảo tính chính xác của dữ liệu. Trong trường hợp nêu trên, khi phép cộng gây ra tràn số nguyên, trình biên dịch không thực hiện đúng việc xóa các bit cao của kết quả, dẫn đến việc bit 1 ở vị trí cao bị ghi vào bộ nhớ lưu trữ, cuối cùng ghi đè lên biến a và làm thay đổi giá trị của biến b thành 1.
SOL-2022-4 InlineAssemblyMemorySideEffects
Lỗi này tồn tại trong các phiên bản biên dịch viên từ >=0.8.13 đến <0.8.15.
Xem xét đoạn mã sau:
độ vững chắc
hợp đồng C {
function f() public pure returns (uint) {
assembly {
mstore(0, 0x42)
}
uint x;
lắp ráp {
x := mload(0)
}
return x;
}
}
Trình biên dịch Solidity trong quá trình chuyển đổi ngôn ngữ Solidity thành mã EVM không chỉ thực hiện dịch đơn giản, mà còn tiến hành phân tích sâu về luồng điều khiển và dữ liệu, thực hiện các tối ưu hóa biên dịch khác nhau để giảm kích thước mã được tạo ra và tối ưu hóa mức tiêu thụ gas trong quá trình thực thi. Loại tối ưu hóa này rất phổ biến trong các trình biên dịch của các ngôn ngữ cao cấp khác nhau, nhưng do các tình huống cần xem xét rất phức tạp, dễ dẫn đến lỗi hoặc lỗ hổng bảo mật.
Lỗ hổng trong đoạn mã trên xuất phát từ các thao tác tối ưu hóa kiểu này. Nếu trong một hàm nào đó có mã sửa đổi dữ liệu tại vị trí bộ nhớ 0, nhưng sau đó không sử dụng dữ liệu đó, thì thực tế có thể loại bỏ mã sửa đổi bộ nhớ 0, từ đó tiết kiệm gas mà không ảnh hưởng đến logic chương trình sau.
Chiến lược tối ưu hóa này không có vấn đề gì, nhưng trong việc triển khai trình biên dịch Solidity cụ thể, loại tối ưu hóa này chỉ được áp dụng cho một khối assembly duy nhất. Đối với mã PoC ở trên, việc ghi và truy cập vào bộ nhớ 0 nằm trong hai khối assembly khác nhau, trong khi trình biên dịch chỉ phân tích và tối ưu hóa khối assembly riêng lẻ. Do không có bất kỳ thao tác đọc nào sau khi ghi vào bộ nhớ 0 trong khối assembly đầu tiên, nên lệnh ghi này được xác định là thừa và sẽ bị loại bỏ, dẫn đến lỗi. Trong phiên bản có lỗ hổng, hàm f() sẽ trả về giá trị 0, trong khi giá trị trả về đúng lẽ ra phải là 0x42.
Lỗ hổng này ảnh hưởng đến các phiên bản biên dịch viên từ >= 0.5.8 đến < 0.8.16.
Xem xét mã sau:
solidity
hợp đồng C {
chức năng f(string[1] calldata a) công cộng tinh khiết trả về (string bộ nhớ) {
trả về abi.decode(abi.encode(a), (string[1]))[0];
}
}
Trong trường hợp bình thường, biến a trả về từ mã trên nên là "aaaa". Nhưng trong phiên bản có lỗ hổng, nó sẽ trả về chuỗi rỗng "".
Nguyên nhân của lỗ hổng này là do Solidity thực hiện thao tác abi.encode trên mảng kiểu calldata, đã sai lầm trong việc dọn dẹp một số dữ liệu, dẫn đến việc sửa đổi các dữ liệu khác ở gần, gây ra sự không nhất quán giữa dữ liệu đã mã hóa và giải mã.
Cần lưu ý rằng, khi thực hiện cuộc gọi bên ngoài và phát sự kiện, Solidity sẽ ngầm mã hóa các tham số bằng abi.encode, do đó xác suất xuất hiện của mã lỗ hổng trên sẽ cao hơn so với cảm giác trực quan.
Đề xuất an toàn
Đối với mối đe dọa từ lỗ hổng biên dịch Solidity, đưa ra các khuyến nghị sau cho các nhà phát triển và nhân viên an ninh:
Đối với nhà phát triển:
Sử dụng phiên bản trình biên dịch Solidity mới hơn. Mặc dù phiên bản mới có thể đưa ra những vấn đề bảo mật mới, nhưng các vấn đề bảo mật đã biết thường ít hơn ở các phiên bản cũ.
Hoàn thiện các trường hợp kiểm tra đơn vị. Phần lớn các lỗi ở cấp trình biên dịch sẽ dẫn đến kết quả thực thi mã không nhất quán với mong đợi. Những vấn đề này rất khó phát hiện qua việc xem xét mã, nhưng dễ dàng bị lộ trong giai đoạn kiểm tra. Tăng cường độ bao phủ mã có thể tối đa hóa việc tránh những vấn đề này.
Cố gắng tránh sử dụng lắp ghép nội tuyến, giải mã abi cho mảng nhiều chiều và cấu trúc phức tạp cũng như các thao tác phức tạp khác. Tránh sử dụng những tính năng mới của ngôn ngữ và các chức năng thử nghiệm mà không có yêu cầu rõ ràng. Phần lớn các lỗ hổng lịch sử liên quan đến các thao tác như lắp ghép nội tuyến và bộ mã hóa abi. Trình biên dịch dễ gặp lỗi hơn khi xử lý các tính năng phức tạp của ngôn ngữ. Mặt khác, các nhà phát triển cũng dễ mắc phải sai lầm khi sử dụng các tính năng mới, dẫn đến các vấn đề an toàn.
Đối với nhân viên an ninh:
Khi tiến hành kiểm tra an toàn mã Solidity, đừng bỏ qua những rủi ro an toàn có thể do trình biên dịch gây ra. Mục kiểm tra tương ứng trong Phân loại điểm yếu hợp đồng thông minh ( SWC ) là SWC-102: Phiên bản trình biên dịch lỗi thời.
Trong quy trình phát triển SDL nội bộ, thúc giục đội ngũ phát triển nâng cấp phiên bản trình biên dịch Solidity, và có thể xem xét việc đưa vào quy trình CI/CD một kiểm tra tự động cho phiên bản trình biên dịch.
Nhưng không cần quá hoảng sợ về các lỗ hổng của trình biên dịch, hầu hết các lỗ hổng của trình biên dịch chỉ được kích hoạt trong các mô hình mã cụ thể, và không phải hợp đồng được biên dịch bằng phiên bản trình biên dịch có lỗ hổng nào cũng nhất định có nguy cơ an toàn, tác động an toàn thực tế cần được đánh giá cụ thể dựa trên tình hình dự án.
Một số tài nguyên hữu ích:
Các bài đăng Cảnh báo An ninh do Nhóm Solidity phát hành định kỳ
Danh sách lỗi được cập nhật định kỳ từ repo chính thức của Solidity
Danh sách lỗi biên dịch của các phiên bản. Có thể đưa vào quá trình CI/CD để tự động kiểm tra phiên bản biên dịch, thông báo các lỗ hổng bảo mật có trong phiên bản hiện tại.
Code của Etherscan, biểu tượng dấu chấm than hình tam giác ở góc trên bên phải có thể chỉ ra các lỗ hổng bảo mật tồn tại trong phiên bản biên dịch hiện tại.
Tóm tắt
Bài viết bắt đầu từ các khái niệm cơ bản về biên dịch, giới thiệu lỗ hổng của trình biên dịch Solidity, phân tích các rủi ro về an ninh có thể xảy ra trong môi trường phát triển Ethereum thực tế, và cung cấp cho các nhà phát triển và nhân viên an ninh một số lời khuyên về an toàn thực tế. Bằng cách hiểu và chú trọng đến lỗ hổng của trình biên dịch, có thể bảo đảm an toàn cho hợp đồng thông minh một cách toàn diện hơn.
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.
18 thích
Phần thưởng
18
9
Chia sẻ
Bình luận
0/400
DAOplomacy
· 16giờ trước
có thể nói là một triển khai không tối ưu từ góc độ quản trị...
Xem bản gốcTrả lời0
LightningLady
· 17giờ trước
Chúng ta có thêm một lỗ hổng biên dịch thì có sao đâu.
Xem bản gốcTrả lời0
SandwichDetector
· 07-10 06:32
Đó chẳng phải là công việc chúng ta làm sao?
Xem bản gốcTrả lời0
GateUser-00be86fc
· 07-08 20:59
Trình biên dịch cũng cần được kiểm toán.
Xem bản gốcTrả lời0
CryptoTherapist
· 07-08 20:58
hãy dành một khoảnh khắc chánh niệm để xử lý sự lo âu của trình biên dịch này... cảm giác như là chấn thương hệ thống cổ điển thật lòng mà nói
Xem bản gốcTrả lời0
CryptoPhoenix
· 07-08 20:56
Mã nguồn còn nguyên bản hơn bản gốc, những gì đã giảm xuống cuối cùng sẽ tái sinh
Xem bản gốcTrả lời0
OnlyOnMainnet
· 07-08 20:50
Lại phải đau đầu sửa mã code rồi
Xem bản gốcTrả lời0
FrogInTheWell
· 07-08 20:47
Cũng tốt là bây giờ không cần viết mã sol nữa.
Xem bản gốcTrả lời0
GasGuzzler
· 07-08 20:30
Lỗ hổng chó lại bắt đầu kêu gọi người làm kiểm toán.
Phân tích lỗ hổng biên dịch Solidity và chiến lược ứng phó
Phân tích chi tiết lỗ hổng biên dịch viên Solidity và chiến lược ứng phó
Trình biên dịch là một trong những thành phần cơ bản của hệ thống máy tính hiện đại. Nó là một chương trình máy tính, chức năng chính là chuyển đổi mã nguồn của ngôn ngữ lập trình bậc cao thành mã lệnh có thể thực thi bởi CPU hoặc máy ảo của máy tính.
Mặc dù hầu hết các nhà phát triển và nhân viên an ninh thường chú trọng vào tính an toàn của mã ứng dụng, nhưng tính an toàn của chính trình biên dịch cũng quan trọng không kém. Là một chương trình máy tính, trình biên dịch cũng có thể tồn tại lỗ hổng bảo mật, trong một số trường hợp có thể gây ra rủi ro an ninh nghiêm trọng. Ví dụ, khi trình duyệt biên dịch và phân tích mã JavaScript phía trước, có thể do lỗ hổng của engine phân tích JavaScript mà người dùng khi truy cập vào trang web độc hại có thể bị kẻ tấn công lợi dụng, cuối cùng dẫn đến việc kiểm soát trình duyệt của nạn nhân thậm chí là hệ điều hành.
Trình biên dịch Solidity cũng tồn tại lỗ hổng bảo mật. Theo cảnh báo an toàn của nhóm phát triển Solidity, đã phát hiện ra vấn đề bảo mật trong nhiều phiên bản khác nhau của trình biên dịch Solidity.
Lỗ hổng trong trình biên dịch Solidity
Chức năng chính của trình biên dịch Solidity là chuyển đổi mã hợp đồng thông minh thành mã lệnh (EVM) cho máy ảo Ethereum. Những mã lệnh EVM này được đóng gói trong giao dịch và tải lên Ethereum, cuối cùng được EVM phân tích và thực thi.
Cần lưu ý rằng lỗ hổng trình biên dịch Solidity khác với lỗ hổng EVM. Lỗ hổng EVM đề cập đến các vấn đề bảo mật phát sinh khi máy ảo thực hiện các lệnh. Do kẻ tấn công có thể tải lên bất kỳ mã nào vào Ethereum, nếu EVM có lỗ hổng bảo mật, điều đó sẽ ảnh hưởng đến toàn bộ mạng Ethereum, có thể dẫn đến từ chối dịch vụ (DoS) thậm chí toàn bộ blockchain bị kẻ tấn công chiếm đoạt. Tuy nhiên, thiết kế EVM tương đối đơn giản, mã lõi được cập nhật không thường xuyên, vì vậy khả năng xảy ra các vấn đề như vậy là tương đối thấp.
Lỗi biên dịch Solidity đề cập đến các vấn đề xảy ra khi trình biên dịch chuyển đổi mã Solidity thành mã EVM. Khác với việc trình duyệt biên dịch và chạy JavaScript trên máy khách của người dùng, quá trình biên dịch Solidity chỉ diễn ra trên máy tính của nhà phát triển hợp đồng thông minh và không được thực thi trên Ethereum. Do đó, lỗi biên dịch Solidity sẽ không ảnh hưởng trực tiếp đến mạng Ethereum.
Một mối nguy hiểm chính của lỗ hổng trình biên dịch Solidity là có thể dẫn đến mã EVM được tạo ra không nhất quán với mong đợi của nhà phát triển. Vì các hợp đồng thông minh trên Ethereum thường liên quan đến tài sản tiền điện tử của người dùng, bất kỳ lỗi hợp đồng nào do trình biên dịch gây ra cũng có thể dẫn đến mất mát tài sản của người dùng, với hậu quả nghiêm trọng.
Các nhà phát triển và các kiểm toán viên hợp đồng có thể chủ yếu chú ý đến các vấn đề thực hiện logic mã hợp đồng, cũng như các vấn đề bảo mật cấp Solidity như tấn công tái nhập và tràn số nguyên. Tuy nhiên, lỗ hổng biên dịch thường khó phát hiện chỉ bằng cách kiểm toán mã nguồn hợp đồng. Cần phải phân tích kết hợp giữa phiên bản trình biên dịch cụ thể và các mẫu mã cụ thể để xác định xem hợp đồng thông minh có bị ảnh hưởng bởi lỗ hổng biên dịch hay không.
Ví dụ về lỗ hổng trình biên dịch Solidity
Dưới đây là một vài ví dụ thực tế về lỗ hổng biên dịch Solidity, cho thấy hình thức cụ thể, nguyên nhân và tác hại của chúng.
SOL-2016-9 HighOrderByteCleanStorage
Lỗ hổng này tồn tại trong các phiên bản biên dịch Solidity sớm hơn (>=0.1.6 <0.4.4).
Xem xét mã sau:
solidity hợp đồng C { uint32 a = 0x12345678; uint32 b = 0; function f() public { a = a + 1; } function run() public view returns (uint) { return b; } }
Biến lưu trữ b không bị thay đổi, vì vậy hàm run() nên trả về giá trị mặc định 0. Tuy nhiên, trong mã được tạo ra bởi trình biên dịch phiên bản có lỗ hổng, run() sẽ trả về 1.
Trong trường hợp không hiểu rõ lỗ hổng của trình biên dịch, các nhà phát triển thông thường rất khó phát hiện lỗi này chỉ bằng việc kiểm tra mã đơn giản. Mặc dù ví dụ này tương đối đơn giản và không gây ra hậu quả nghiêm trọng, nhưng nếu biến b được sử dụng cho xác thực quyền, ghi chép tài sản và các mục đích khác, sự không nhất quán này có thể dẫn đến hậu quả rất nghiêm trọng.
Nguyên nhân gây ra hiện tượng này là do EVM sử dụng máy ảo theo kiểu ngăn xếp, mỗi phần tử trong ngăn xếp đều có kích thước 32 byte ( tức là kích thước của biến uint256 ). Mặt khác, mỗi slot trong bộ nhớ lưu trữ (storage) cũng có kích thước 32 byte. Ngôn ngữ Solidity hỗ trợ các kiểu dữ liệu có kích thước nhỏ hơn 32 byte như uint32, và khi biên dịch, trình biên dịch cần thực hiện các thao tác xóa thích hợp trên các bit cao hơn ( clean up ) để đảm bảo tính chính xác của dữ liệu. Trong trường hợp nêu trên, khi phép cộng gây ra tràn số nguyên, trình biên dịch không thực hiện đúng việc xóa các bit cao của kết quả, dẫn đến việc bit 1 ở vị trí cao bị ghi vào bộ nhớ lưu trữ, cuối cùng ghi đè lên biến a và làm thay đổi giá trị của biến b thành 1.
SOL-2022-4 InlineAssemblyMemorySideEffects
Lỗi này tồn tại trong các phiên bản biên dịch viên từ >=0.8.13 đến <0.8.15.
Xem xét đoạn mã sau:
độ vững chắc hợp đồng C { function f() public pure returns (uint) { assembly { mstore(0, 0x42) } uint x; lắp ráp { x := mload(0) } return x; } }
Trình biên dịch Solidity trong quá trình chuyển đổi ngôn ngữ Solidity thành mã EVM không chỉ thực hiện dịch đơn giản, mà còn tiến hành phân tích sâu về luồng điều khiển và dữ liệu, thực hiện các tối ưu hóa biên dịch khác nhau để giảm kích thước mã được tạo ra và tối ưu hóa mức tiêu thụ gas trong quá trình thực thi. Loại tối ưu hóa này rất phổ biến trong các trình biên dịch của các ngôn ngữ cao cấp khác nhau, nhưng do các tình huống cần xem xét rất phức tạp, dễ dẫn đến lỗi hoặc lỗ hổng bảo mật.
Lỗ hổng trong đoạn mã trên xuất phát từ các thao tác tối ưu hóa kiểu này. Nếu trong một hàm nào đó có mã sửa đổi dữ liệu tại vị trí bộ nhớ 0, nhưng sau đó không sử dụng dữ liệu đó, thì thực tế có thể loại bỏ mã sửa đổi bộ nhớ 0, từ đó tiết kiệm gas mà không ảnh hưởng đến logic chương trình sau.
Chiến lược tối ưu hóa này không có vấn đề gì, nhưng trong việc triển khai trình biên dịch Solidity cụ thể, loại tối ưu hóa này chỉ được áp dụng cho một khối assembly duy nhất. Đối với mã PoC ở trên, việc ghi và truy cập vào bộ nhớ 0 nằm trong hai khối assembly khác nhau, trong khi trình biên dịch chỉ phân tích và tối ưu hóa khối assembly riêng lẻ. Do không có bất kỳ thao tác đọc nào sau khi ghi vào bộ nhớ 0 trong khối assembly đầu tiên, nên lệnh ghi này được xác định là thừa và sẽ bị loại bỏ, dẫn đến lỗi. Trong phiên bản có lỗ hổng, hàm f() sẽ trả về giá trị 0, trong khi giá trị trả về đúng lẽ ra phải là 0x42.
SOL-2022-6 AbiReencodingHeadOverflowWithStaticArrayCleanup
Lỗ hổng này ảnh hưởng đến các phiên bản biên dịch viên từ >= 0.5.8 đến < 0.8.16.
Xem xét mã sau:
solidity hợp đồng C { chức năng f(string[1] calldata a) công cộng tinh khiết trả về (string bộ nhớ) { trả về abi.decode(abi.encode(a), (string[1]))[0]; } }
Trong trường hợp bình thường, biến a trả về từ mã trên nên là "aaaa". Nhưng trong phiên bản có lỗ hổng, nó sẽ trả về chuỗi rỗng "".
Nguyên nhân của lỗ hổng này là do Solidity thực hiện thao tác abi.encode trên mảng kiểu calldata, đã sai lầm trong việc dọn dẹp một số dữ liệu, dẫn đến việc sửa đổi các dữ liệu khác ở gần, gây ra sự không nhất quán giữa dữ liệu đã mã hóa và giải mã.
Cần lưu ý rằng, khi thực hiện cuộc gọi bên ngoài và phát sự kiện, Solidity sẽ ngầm mã hóa các tham số bằng abi.encode, do đó xác suất xuất hiện của mã lỗ hổng trên sẽ cao hơn so với cảm giác trực quan.
Đề xuất an toàn
Đối với mối đe dọa từ lỗ hổng biên dịch Solidity, đưa ra các khuyến nghị sau cho các nhà phát triển và nhân viên an ninh:
Đối với nhà phát triển:
Sử dụng phiên bản trình biên dịch Solidity mới hơn. Mặc dù phiên bản mới có thể đưa ra những vấn đề bảo mật mới, nhưng các vấn đề bảo mật đã biết thường ít hơn ở các phiên bản cũ.
Hoàn thiện các trường hợp kiểm tra đơn vị. Phần lớn các lỗi ở cấp trình biên dịch sẽ dẫn đến kết quả thực thi mã không nhất quán với mong đợi. Những vấn đề này rất khó phát hiện qua việc xem xét mã, nhưng dễ dàng bị lộ trong giai đoạn kiểm tra. Tăng cường độ bao phủ mã có thể tối đa hóa việc tránh những vấn đề này.
Cố gắng tránh sử dụng lắp ghép nội tuyến, giải mã abi cho mảng nhiều chiều và cấu trúc phức tạp cũng như các thao tác phức tạp khác. Tránh sử dụng những tính năng mới của ngôn ngữ và các chức năng thử nghiệm mà không có yêu cầu rõ ràng. Phần lớn các lỗ hổng lịch sử liên quan đến các thao tác như lắp ghép nội tuyến và bộ mã hóa abi. Trình biên dịch dễ gặp lỗi hơn khi xử lý các tính năng phức tạp của ngôn ngữ. Mặt khác, các nhà phát triển cũng dễ mắc phải sai lầm khi sử dụng các tính năng mới, dẫn đến các vấn đề an toàn.
Đối với nhân viên an ninh:
Khi tiến hành kiểm tra an toàn mã Solidity, đừng bỏ qua những rủi ro an toàn có thể do trình biên dịch gây ra. Mục kiểm tra tương ứng trong Phân loại điểm yếu hợp đồng thông minh ( SWC ) là SWC-102: Phiên bản trình biên dịch lỗi thời.
Trong quy trình phát triển SDL nội bộ, thúc giục đội ngũ phát triển nâng cấp phiên bản trình biên dịch Solidity, và có thể xem xét việc đưa vào quy trình CI/CD một kiểm tra tự động cho phiên bản trình biên dịch.
Nhưng không cần quá hoảng sợ về các lỗ hổng của trình biên dịch, hầu hết các lỗ hổng của trình biên dịch chỉ được kích hoạt trong các mô hình mã cụ thể, và không phải hợp đồng được biên dịch bằng phiên bản trình biên dịch có lỗ hổng nào cũng nhất định có nguy cơ an toàn, tác động an toàn thực tế cần được đánh giá cụ thể dựa trên tình hình dự án.
Một số tài nguyên hữu ích:
Tóm tắt
Bài viết bắt đầu từ các khái niệm cơ bản về biên dịch, giới thiệu lỗ hổng của trình biên dịch Solidity, phân tích các rủi ro về an ninh có thể xảy ra trong môi trường phát triển Ethereum thực tế, và cung cấp cho các nhà phát triển và nhân viên an ninh một số lời khuyên về an toàn thực tế. Bằng cách hiểu và chú trọng đến lỗ hổng của trình biên dịch, có thể bảo đảm an toàn cho hợp đồng thông minh một cách toàn diện hơn.