Wskazówki dotyczące bezpieczeństwa kontraktów inteligentnych
Postępuj zgodnie z tymi ogólnymi zaleceniami, aby tworzyć bezpieczniejsze inteligentne kontrakty.
Wytyczne dotyczące projektowania
Projekt kontraktu powinien być omówiony z wyprzedzeniem, przed napisaniem jakiejkolwiek linijki kodu.
Dokumentacja i specyfikacje
Dokumentacja może być pisana na różnych poziomach i powinna być aktualizowana w trakcie realizacji umów:
- Prosty opis systemu w języku angielskim, przedstawiający działanie kontraktów i wszelkie założenia dotyczące bazy kodu.
- Diagramy schematów i architektury, w tym interakcje kontraktów i maszyna stanu systemu. Drukarki Slither mogą pomóc w wygenerowaniu tych schematów.
- Dokładna dokumentacja kodu, format Natspec może być używany do Solidity.
Obliczenia on-chain vs off-chain
- Zachowaj jak najwięcej kodu off-chain. Zatrzymaj niewielką warstwę on-chain. Wstępnie przetwarzaj dane z kodem off-chain w taki sposób, aby weryfikacja on-chain była prosta. Potrzebujesz uporządkowanej listy? Posortuj listę off-chain, a następnie sprawdź tylko jej kolejność on-chain.
Możliwość uaktualnienia
Omówiliśmy różne rozwiązania dotyczące możliwości uaktualnień w poście na blogu. Dokonaj rozważnego wyboru, czy wspierać możliwość uaktualniania, czy nie, przed napisaniem jakiegokolwiek kodu. Decyzja wpłynie na sposób, w jaki ustrukturyzujesz kod. Generalnie zalecamy:
- Przedkładanie migracji kontraktu nad możliwość uaktualnienia. System migracji ma wiele takich samych zalet, jak możliwość uaktualnienia, bez ich wad.
- Używanie wzorca separacji danych zamiast wzorca delegatecalproxy. Jeśli projekt ma wyraźną separację abstrakcji, możliwość uaktualnienia przy użyciu separacji danych będzie wymagać tylko kilku dostosowań. Delegatecallproxy wymaga wiedzy specjalistycznej o EVM i jest wysoce podatny na błędy.
- Dokumentowanie procedury migracji/uaktualnienia przed wdrożeniem. Jeśli będziesz musiał reagować w stresie bez żadnych wytycznych, popełnisz błędy. Zapisz procedurę do wykonania z wyprzedzeniem. Powinna ona obejmować:
- Wywołania inicjujące nowe kontrakty
- Gdzie są przechowywane klucze i jak uzyskać dostęp do nich
- Jak sprawdzić wdrożenie! Opracowanie i przetestowanie skryptu po wdrożeniu.
Wytyczne dotyczące wdrażania
Poszukaj prostoty. Zawsze używaj najprostszego rozwiązania, które pasuje do Twojego celu. Każdy członek twojego zespołu powinien być w stanie zrozumieć Twoje rozwiązanie.
Skład funkcji
Architektura Twojej bazy kodu powinna ułatwić sprawdzenie twojego kodu. Unikaj wyborów architektonicznych, które zmniejszają zdolność rozumowania o jego poprawności.
- Podziel logikę swojego systemu poprzez wiele umów lub grupowanie podobnych funkcji (na przykład uwierzytelniające, arytmetyczne, ...).
- Pisz małe funkcje o wyraźnym celu celu. To ułatwi sprawdzenie i umożliwi testowanie poszczególnych komponentów.
Dziedziczenie
- Zachowaj dziedziczenie do zarządzania. Dziedzictwo powinno być używane do dzielenia logiki, jednak Twój projekt powinien mieć na celu zminimalizowanie głębokości i szerokości drzewa dziedziczenia.
- Użyj drukarki dziedziczenia Slither'a, aby sprawdzić hierarchię kontraktów. Drukarka dziedziczenia pomoże Ci sprawdzić rozmiar hierarchii.
Zdarzenia
- Rejestruj wszystkie kluczowe operacje. Zdarzenia pomogą debugować kontrakt podczas jego oprawcowywania i będą go monitorować po wdrożeniu.
Unikanie znanych pułapek
- Bądź świadomy najczęstszych problemów z bezpieczeństwem. Istnieje wiele zasobów online do poznania wspólnych problemów, takich jak Ethernaut CTF, Zajmij Etherlub Nie tak inteligentne kontrakty.
- Zwróć uwagę na sekcje ostrzeżeń w dokumentacji Solidity. Sekcje z ostrzeżeniami poinformują Cię o nieoczywistym zachowaniu języka.
Zależności
- Używaj dobrze przetestowanych bibliotek. Importowanie kodu z dobrze przetestowanych bibliotek zmniejszy prawdopodobieństwo, że napiszesz kod z błędami. Jeśli chcesz napisać kontrakt ERC20, użyj OpenZeppelin.
- Użyj menedżera zależności; unikaj kopiowania kodu. Jeśli opierasz się na źródle zewnętrznym, musisz na bieżąco aktualizować je w stosunku do źródła oryginalnego.
Testy i weryfikacja
- Zapisz dokładne testy jednostkowe. Rozległy zestaw testowy ma kluczowe znaczenie dla budowy oprogramowania wysokiej jakości.
- Napisz niestandardowe kontrole i właściwości dla narzędzi Slither, Echidna i Manticore. Automatyczne narzędzia pomogą zapewnić bezpieczeństwo umowy. Przejrzyj resztę tego przewodnika, aby dowiedzieć się, jak pisać skuteczne kontrole i właściwości.
- Użyj crytic.io. Crytic integruje się z Githubem, zapewnia dostęp do prywatnych detektorów Slither, i uruchamia niestandardowe kontrole właściwości z Echidny.
Solidity
- Favor Solidity 0.5 ponad 0.4 i 0.6. Naszym zdaniem Solidity 0.5 jest bezpieczniejszy i ma lepsze wbudowane praktyki niż 0.4. Solidity 0.6 okazała się zbyt niestabilna do produkcji i wymaga czasu, aby dojrzeć.
- Użyj stabilnej wersji do kompilacji; użyj najnowszej wersji, aby sprawdzić ostrzeżenia. Sprawdź, czy Twój kod nie ma zgłoszonych problemów z najnowszą wersją kompilatora. Solidity ma jednak szybki cykl wydawniczy i ma historię błędów kompilatora, więc nie zalecamy wdrażania najnowszej wersji (zobacz zalecenie wersji Solc).
- Nie używaj wbudowanego asemblera. Asemblacja wymaga wiedzy fachowej na temat EVM. Nie pisz kodu EVM, jeśli nie opanowałeś żółtej księgi.
Wytyczne dotyczące wdrażania
Po opracowaniu i wdrożeniu kontraktu:
- Monitoruj swoje kontrakty. Obserwuj dzienniki i bądź gotowy do reagowania w przypadku naruszenia kontraktu lub portfela.
- Dodaj swoje dane kontaktowe do blockchain-security-contacts. Ta lista pomaga firmom zewnętrznym skontaktować się z Tobą w przypadku wykrycia luki w zabezpieczeniach.
- Zabezpiecz portfele uprzywilejowanych użytkowników. Postępuj zgodnie z naszymi najlepszymi praktykami, jeśli przechowujesz klucze w portfelach sprzętowych.
- Opracuj plan reakcji na incydent. Weź pod uwagę, że Twoje inteligentne kontrakty mogą zostać naruszone. Nawet jeśli twoje kontrakty są wolne od błędów, atakujący może przejąć kontrolę nad kluczami właściciela umowy.