Chúng ta vừa thấy cách một lỗ hổng nhỏ dẫn đến tổn thất tài chính (ở mức độ khác nhau), theo cách tương tự như cách các hợp đồng thông minh được phát triển trên Solidity dễ bị tấn công khác nhau và chưa biết. Những kẻ khai thác lợi dụng các lỗi và sơ hở để xem trộm các hợp đồng thông minh và thao túng chúng để thực hiện các cuộc tấn công. Ở đây chúng tôi trình bày danh sách toàn diện về 5 lỗi thường gặp nhất trong ngôn ngữ lập trình Solidity.
Tại QuillAudits, chúng tôi tuân theo phương pháp thích ứng để nắm được ý chính của mọi vụ hack và triển khai các bài học của nó trên các hợp đồng thông minh trong tương lai để tránh bất kỳ mối đe dọa tiềm ẩn nào.
Lỗi trong ngôn ngữ lập trình solidity
1. Cuộc gọi bên ngoài không được chọn
Chúng tôi đang giải quyết vấn đề này ngay từ đầu vì đây là một trong những cạm bẫy của Solidity thường thấy nhất. Nói chung, để gửi ether đến bất kỳ tài khoản bên ngoài nào được thực hiện thông qua chuyển khoản() chức năng. Ngoài ra, hai chức năng được sử dụng rộng rãi nhất để thực hiện cuộc gọi bên ngoài là; gọi()và gửi (), ở đây chủ yếu là gọi() hàm được sử dụng rộng rãi để thực hiện các cuộc gọi bên ngoài linh hoạt bởi các nhà phát triển.
Thông qua gọi() và gửi () các hàm trả về một giá trị boolean xác định xem cuộc gọi có thành công hay không. Vì vậy, trong trường hợp này, nếu bất kỳ chức năng nào gọi() or gửi () không thực hiện được nhiệm vụ, chúng sẽ hoàn nguyên với sai. Do đó, nếu nhà phát triển không kiểm tra chéo giá trị trả về, nó sẽ trở thành một cạm bẫy.
Lỗ hổng bảo mật
Hãy xem xét ví dụ dưới đây:
hợp đồng xổ số {
boolpublic payedOut = false;
địa chỉ người chiến thắng công khai;
uintpublic winAmount;
//… chức năng bổ sung ở đây
function sendToWinner () public {
yêu cầu (! payedOut);
winner.send (winAmount);
payedOut = true;
}
function rút luiLeftOver () public {
yêu cầu (payedOut);
msg.sender.send (this.balance);
}
}
Trong hợp đồng giống như xổ số ở trên, chúng ta có thể thấy rằng một người chiến thắng nhận số tiền thắng của ether để lại một phần nhỏ còn lại sẽ được rút ra khỏi bất kỳ tác nhân bên ngoài nào.
Ở đây, cạm bẫy cho hợp đồng tồn tại ở dòng [11], trong đó gửi được sử dụng mà không xác nhận chéo phản hồi. Trong ví dụ trên, một người chiến thắng mà giao dịch không thành công (do thiếu Gas hoặc nếu đó là một hợp đồng cố tình ném vào chức năng dự phòng), cho phép đã thanh toán được đặt thành đúng bất kể giao dịch ether có thành công hay không. Trong trường hợp này, bất kỳ người khai thác nào cũng có thể rút người chiến thắng chiến thắng thông qua rút lại chức năng.
Cách tiếp cận của QuillAudit
Nhóm các nhà phát triển nội bộ của chúng tôi đã khắc phục lỗi này bằng cách sử dụng [chuyển khoản] chức năng thay vì [gửi] , vì [chuyển] sẽ hoàn nguyên nếu giao dịch bên ngoài hoàn nguyên. Và nếu bạn đang sử dụng [send], hãy luôn kiểm tra chéo giá trị trả về.
Một trong những cách tiếp cận mạnh mẽ mà chúng tôi làm theo là sử dụng [mô hình rút tiền]. Ở đây, chúng tôi cô lập một cách hợp lý chức năng gửi bên ngoài khỏi phần còn lại của cơ sở mã và đặt hàng loạt các giao dịch có khả năng thất bại lên người dùng cuối, vì anh ta là người gọi hàm rút tiền.
2. Vào lại
Các hợp đồng thông minh Ethereum gọi và sử dụng các mã từ các hợp đồng bên ngoài khác và để thực hiện điều này, các hợp đồng bắt buộc phải gửi các lệnh gọi bên ngoài. Các cuộc gọi bên ngoài này dễ bị tấn công và dễ bị tấn công, một cuộc tấn công như vậy đã diễn ra gần đây trong trường hợp hack DAO.
Lỗ hổng bảo mật
Những kẻ tấn công thực hiện các cuộc tấn công như vậy khi một hợp đồng gửi ether đến một địa chỉ không xác định. Trong trường hợp này, kẻ tấn công có thể tạo hợp đồng tại một địa chỉ bên ngoài sở hữu mã độc trong chức năng dự phòng và mã độc này sẽ được gọi khi hợp đồng gửi ether đến địa chỉ này.
Thực tế: Thuật ngữ 'Reentrancy' đã được đặt ra từ thực tế là khi một hợp đồng độc hại bên ngoài gọi một chức năng trên hợp đồng dễ bị tấn công và sau đó đường dẫn thực thi mã 'nhập lại' nó.
Hãy xem xét ví dụ bên dưới, đó là một kho tiền Ethereum cho phép người gửi tiền chỉ rút 1 ether mỗi tuần.
hợp đồng EtherStore {
uint256 rút tiền công khaiLimit = 1 ether;
ánh xạ (address => uint256) public lastWithdrawTime;
ánh xạ (địa chỉ => uint256) số dư công khai;
chức năng tiền gửi
số dư [msg.sender] + = msg.value;
}
chức năng rút tiền (uint256 _weiToWithdraw) công khai {
request (số dư [msg.sender]> = _weiToWithdraw);
// giới hạn việc rút tiền
yêu cầu (_weiToWithdraw <= rút lạiLimit);
// giới hạn thời gian được phép rút
request (now> = lastWithdrawTime [msg.sender] + 1 tuần);
request (msg.sender.call.value (_weiToWithdraw) ());
số dư [msg.sender] - = _weiToWithdraw;
lastWithdrawTime [msg.sender] = bây giờ;
}
}
Trong hợp đồng trên, chúng tôi có hai chức năng công khai, [ký quỹ] và [rút tiền]. [Khoản tiền gửi] được sử dụng để tăng số dư của người gửi, trong khi [rút tiền] chỉ định số tiền được rút. Trong trường hợp này, sẽ thành công nếu số tiền cần rút nhỏ hơn 1 ether.
Cạm bẫy ở đây nằm ở dòng [17] nơi diễn ra quá trình chuyển ether. Kẻ tấn công có thể tạo một hợp đồng độc hại với địa chỉ hợp đồng của [EtherStores] làm tham số khởi tạo duy nhất. Điều này sẽ làm cho [etherStore] trở thành một biến công khai, do đó dễ bị tấn công hơn.
Cách tiếp cận của QuilllAudit
Chúng tôi tuân theo các kỹ thuật khác nhau để tránh các lỗ hổng tiềm ẩn trong các hợp đồng thông minh. Cách đầu tiên và tốt nhất có thể là sử dụng chức năng [chuyển] tích hợp khi chuyển ether sang bất kỳ hợp đồng bên ngoài nào.
Thứ hai, điều quan trọng là phải đảm bảo rằng tất cả các thay đổi logic trong các biến trạng thái phải được thực hiện trước khi gửi ether ra khỏi hợp đồng. Trong ví dụ [EtherStore], các dòng [18] và [19] nên được đặt trước dòng [17].
Một kỹ thuật thứ ba cũng có thể được sử dụng để ngăn chặn các cuộc gọi nhập lại; thông qua việc giới thiệu một mutex. Nó là một sự bổ sung của một biến trạng thái sẽ khóa hợp đồng trong quá trình thực thi mã.
3. Chế độ hiển thị mặc định
Có các chỉ định khả năng hiển thị cho các chức năng chúng tôi sử dụng trong Solidity và chúng quy định cách chúng có thể được gọi. Đó là khả năng hiển thị xác định việc gọi các chức năng; bên ngoài bởi người dùng, bằng các hợp đồng dẫn xuất khác, chỉ nội bộ hoặc chỉ bên ngoài. Hãy cùng chúng tôi xem xét việc sử dụng sai các chỉ số khả năng hiển thị có thể gây ra lỗ hổng lớn trong các hợp đồng thông minh như thế nào.
Lỗ hổng bảo mật
Theo mặc định, khả năng hiển thị của hàm là [công khai], do đó người dùng bên ngoài có thể gọi các hàm không có khả năng hiển thị cụ thể. Lỗi phát sinh khi các nhà phát triển quên chỉ định khả năng hiển thị trên các chức năng phải là riêng tư (hoặc có thể được gọi trong chính hợp đồng). Ví dụ;
hợp đồng HashForEther {
function rút luiWinnings () {
// Người chiến thắng nếu 8 ký tự hex cuối cùng của địa chỉ là 0
request (uint32 (msg.sender) == 0);
_sendWinnings ();
}
function _sendWinnings () {
msg.sender.transfer (this.balance);
}
}
Hợp đồng trên là một trò chơi tiền thưởng đoán địa chỉ đơn giản. Trong điều này, chúng ta có thể thấy rằng khả năng hiển thị của các hàm không được chỉ định, cụ thể là hàm [_sendWinnings] là [public] (theo mặc định), do đó, nó có thể được gọi thông qua bất kỳ địa chỉ nào để ăn cắp tiền thưởng.
Cách tiếp cận của QuillAudit
Nhóm nội bộ của chúng tôi bao gồm các nhà phát triển dày dạn kinh nghiệm, những người luôn tuân theo các phương pháp kiểm toán tốt nhất, ở đây, mức độ hiển thị của các chức năng nên được chỉ định rõ ràng, ngay cả khi chúng được công khai, cần được đề cập đến.
4. Bảo vệ việc sử dụng các công cụ xây dựng
Nói chung, Constructors được gọi là các chức năng đặc biệt được sử dụng để thực hiện các tác vụ quan trọng và đặc quyền trong khi khởi tạo các hợp đồng. Trước Solidity [v0.4.22], các constructor có cùng tên được sử dụng bởi hợp đồng chứa chúng. Bây giờ, hãy xem xét một trường hợp tên hợp đồng được thay đổi trong giai đoạn phát triển nhưng tên phương thức khởi tạo vẫn được giữ nguyên, lỗ hổng này cũng có thể cung cấp cho những kẻ tấn công một cách dễ dàng vào hợp đồng thông minh của bạn.
Lỗ hổng bảo mật
Nó có thể dẫn đến hậu quả nghiêm trọng nếu tên hợp đồng được sửa đổi nhưng tên của nhà xây dựng không thay đổi. Ví dụ:
hợp đồng OwnerWallet {
địa chỉ chủ sở hữu công khai;
// người xây dựng
function ownerWallet (address _owner) public {
chủ sở hữu = _owner;
}
// Dự phòng. Thu ete.
hàm () phải trả {}
hàm rút lại () công khai {
request (msg.sender == chủ sở hữu);
msg.sender.transfer (this.balance);
}
}
Trong hợp đồng trên, chúng ta có thể thấy rằng chỉ chủ sở hữu mới có thể rút ether thông qua việc gọi hàm [rút]. Ở đây, lỗ hổng xảy ra do hàm tạo được đặt tên khác với hợp đồng (chữ cái đầu tiên khác!). Do đó, người khai thác có thể gọi hàm [ownerWallet] và ủy quyền cho mình với tư cách là chủ sở hữu, sau đó rút tất cả ether trong hợp đồng bằng cách gọi [rút].
Cách tiếp cận của QuillAudit
Chúng tôi tuân thủ phiên bản [0.4.22] của trình biên dịch Solidity. Phiên bản này đã giới thiệu một từ khóa; [constructor] yêu cầu tên của hàm phải khớp với tên hợp đồng.
5. Xác thực Tx.Origin
Ở đây, [Tx.Origin] là biến toàn cục của Solidity, nó chứa địa chỉ của tài khoản đã thực hiện lệnh gọi hoặc giao dịch ban đầu. Không thể sử dụng biến này để xác thực, vì làm như vậy khiến hợp đồng dễ bị tấn công lừa đảo.
Lỗ hổng bảo mật
Các hợp đồng ủy quyền cho người dùng thông qua biến [tx.origin] có nguy cơ bị tấn công từ bên ngoài, dẫn đến việc người dùng thực hiện các hành động được xác thực đối với hợp đồng bị sai sót. Hãy xem xét ví dụ dưới đây:
hợp đồng Phishable {
địa chỉ chủ sở hữu công khai;
hàm tạo (địa chỉ _owner) {
chủ sở hữu = _owner;
}
function () phải trả bên ngoài {} // thu tiền ether
function rút ra khỏi địa chỉ (address _recipient) public {
request (tx.origin == chủ sở hữu);
_recipient.transfer (this.balance);
}
}
Ở dòng [11], hợp đồng cho phép hàm [rút lại] với sự trợ giúp của [tx.origin].
Cách tiếp cận của QuillAudit
Chúng tôi thường tránh sử dụng [tx.origin] để ủy quyền trong các hợp đồng thông minh. Mặc dù, việc sử dụng [tx.origin] không bị nghiêm cấm nhưng nó có một số trường hợp sử dụng cụ thể. Chúng ta có thể sử dụng [tx.origin] để từ chối các hợp đồng bên ngoài gọi hợp đồng hiện tại, nó có thể được thực thi với [request] có dạng [request (tx.origin == msg.sender)]. Nó được thực hiện để tránh việc gọi các hợp đồng trung gian để gọi hợp đồng hiện tại, điều này giới hạn hợp đồng ở các địa chỉ không mã thông thường.
Kết thúc cuối cùng
Chúng tôi đã trình bày toàn diện năm cạm bẫy phổ biến trong ngôn ngữ Solidity. Trong khi phát triển các hợp đồng thông minh, chúng ta không được quên rằng chúng là bất biến theo thiết kế, có nghĩa là một khi chúng ta tạo chúng, không có cách nào để vá mã nguồn.
Điều này đặt ra một thách thức lớn cho các nhà phát triển trong việc tận dụng các công cụ kiểm tra và kiểm tra bảo mật sẵn có trước khi triển khai.
Việc phát hiện ra các mối đe dọa độc hại tiềm ẩn đối với các hợp đồng thông minh và một số rủi ro mà chúng tôi đã đề cập ở trên được thực hiện theo một cách rất độc đáo và mạnh mẽ bởi nhóm chuyên gia kiểm toán nội bộ của chúng tôi. Tại QuillAudits, chúng tôi nỗ lực hết sức vào việc nghiên cứu bảo mật để giữ cho hợp đồng của bạn được cập nhật với tất cả các phương pháp bảo mật phần mềm nhằm giữ cho hợp đồng của bạn an toàn và bảo mật.
Liên hệ với QuillHash
Với sự hiện diện trong ngành nhiều năm, QuillHash đã cung cấp các giải pháp doanh nghiệp trên toàn cầu. QuillHash với đội ngũ chuyên gia là công ty phát triển blockchain hàng đầu cung cấp các giải pháp ngành khác nhau bao gồm DeFi Enterprise, Nếu bạn cần bất kỳ hỗ trợ nào trong việc kiểm tra hợp đồng thông minh, vui lòng liên hệ với các chuyên gia của chúng tôi ở đây!
Theo dõi QuillHash để cập nhật thêm
Nguồn: https://blog.quillhash.com/2021/06/04/top-5-common-errors-in-solidity-programming-language/
- 11
- Tài khoản
- Lợi thế
- Tất cả
- kiểm toán
- Xác thực
- ủy quyền
- BEST
- blockchain
- Bug
- lỗi
- cuộc gọi
- trường hợp
- Nguyên nhân
- thách thức
- mã
- Chung
- công ty
- hợp đồng
- hợp đồng
- Current
- DAO
- Defi
- Thiết kế
- Nhà phát triển
- phát triển
- Phát triển
- Doanh nghiệp
- Ether
- ethereum
- Sự kiện
- các chuyên gia
- tài chính
- Tên
- theo
- hình thức
- Miễn phí
- chức năng
- tương lai
- trò chơi
- GAS
- Toàn cầu
- tuyệt vời
- tấn
- tại đây
- Độ đáng tin của
- HTTPS
- lớn
- Bao gồm
- ngành công nghiệp
- IT
- Ngôn ngữ
- dẫn
- hàng đầu
- Dòng
- Danh sách
- Trận đấu
- Nền tảng khác
- chủ sở hữu
- Vá
- Họa tiết
- Lừa đảo
- tấn công lừa đảo
- quy định
- trình bày
- riêng
- Lập trình
- công khai
- kéo
- nghiên cứu
- phản ứng
- REST của
- an toàn
- an ninh
- DỊCH VỤ
- định
- Đơn giản
- nhỏ
- thông minh
- hợp đồng thông minh
- Hợp đồng thông minh
- So
- Phần mềm
- sự vững chắc
- Giải pháp
- Tiểu bang
- thành công
- Kiểm tra
- Nguồn
- các mối đe dọa
- thời gian
- hàng đầu
- top 5
- giao dịch
- Giao dịch
- us
- Người sử dụng
- giá trị
- Vault
- khả năng hiển thị
- Lỗ hổng
- dễ bị tổn thương
- Dễ bị tổn thương
- tuần
- CHÚNG TÔI LÀ
- ở trong
- năm