Przejdź do głównej zawartości
Change page

Bezpieczeństwo inteligentnych kontraktów

Strona ostatnio zaktualizowana: 14 lutego 2026

Inteligentne kontrakty są ekstremalnie elastyczne oraz zdolne kontrolować duże ilości wartości oraz danych oraz dane stosując niezmienną logikę opartą na kodzie wdrożonym na blockchainie. Powstał dzięki temu tętniący życiem ekosystem niewymagających zaufania i zdecentralizowanych aplikacji, które zapewniają szereg przewag wobec przestarzałych systemów. Stwarzają one również okazje dla napastników szukających zysku poprzez wykorzystanie słabych punktów inteligentnych kontraktów.

Publiczne blockchainy takie jak Ethereum, jeszcze bardziej komplikują zagadnienie zabezpieczeń inteligentnych kontraktów. Kod wdrożonego kontraktu zazwyczaj nie może zostać zmieniony w celu załatania luk w zabezpieczeniach, podczas gdy aktywa skradzione z inteligentnych kontraktów są niezwykle trudne do wyśledzenia i w większości nie do odzyskania ze względu na niezmienność.

Źródła podają różne kwoty, ale można oszacować, że suma środków skradzionych lub straconych w wyniku defektów zabezpieczeń w inteligentnych kontraktach przekracza 1 miliard dolarów. Obejmuje to głośne incydenty, takie jak atak na DAOopens in a new tab (skradziono 3,6 mln ETH, wartych dziś ponad 1 mld USD), atak na portfel wielopodpisowy Parityopens in a new tab (hakerzy ukradli 30 mln USD) oraz problem z zamrożonym portfelem Parityopens in a new tab (ponad 300 mln USD w ETH zablokowane na zawsze).

Wspomniane wcześniej problemy stwarzają programistom konieczność poświęcania uwagi i środków na tworzenie zabezpieczonych, solidnych i odpornych inteligentnych kontraktów. Bezpieczeństwo inteligentnych kontraktów jest poważną sprawą, o której powinien uczyć się każdy programista. W tym przewodniku omówione zostaną zagadnienia związane z bezpieczeństwem skierowane dla programistów Ethereum oraz zasoby ulepszania zabezpieczeń inteligentnych kontraktów.

Wymagania wstępne

Przed zajęciem się kwestiami bezpieczeństwa upewnij się, że znasz podstawy tworzenia inteligentnych kontraktów.

Wytyczne dotyczące budowania bezpiecznych inteligentnych kontraktów Ethereum

1. Zaprojektuj odpowiednie mechanizmy kontroli dostępu

W inteligentnych kontraktach funkcje oznaczone jako public lub external mogą być wywoływane przez dowolne konta zewnętrzne (EOA) lub konta kontraktowe. Określenie publicznej widoczności funkcji jest konieczne, jeśli chcesz, aby inni mogli wchodzić w interakcje z twoim kontraktem. Funkcje oznaczone jako private mogą być jednak wywoływane tylko przez funkcje w ramach danego inteligentnego kontraktu, a nie przez konta zewnętrzne. Udzielanie każdemu użytkownikowi sieci dostępu do funkcji kontraktu może stwarzać problemy, szczególnie jeśli oznacza to, że każdy może przeprowadzać wrażliwe czynności (np. mintowanie nowych tokenów).

Aby uniemożliwić nieautoryzowane użycie funkcji inteligentnego kontraktu, niezbędna jest implementacja zabezpieczeń kontroli dostępu. Mechanizmy kontroli dostępu ograniczają możliwość użycia określonych funkcji inteligentnego kontraktu podmiotom innym niż aprobowane, czyli te konta, które odpowiedzialne są za zarządzanie kontraktem. Wzorce Ownable oraz kontroli opartej na rolach to dwa przydatne schematy implementacji kontroli dostępu w inteligentnych kontraktach:

Wzorzec Ownable

We wzorcu własności, adres oznaczony jest jako "właściciel" kontraktu podczas procesu tworzenia kontraktu. Funkcje chronione mają przypisany modyfikator OnlyOwner, który zapewnia, że kontrakt uwierzytelnia tożsamość adresu wywołującego przed wykonaniem funkcji. Wywołania funkcji objętych ochroną pochodzące od innych adresów oprócz właściciela kontraktu są zawsze pomijane, co uniemożliwia niechciany dostęp.

Kontrola dostępu oparta na rolach

Zarejestrowanie pojedynczego adresu jako Owner w inteligentnym kontrakcie wprowadza ryzyko centralizacji i stanowi pojedynczy punkt awarii. Jeśli klucze dostępu konta właściciela wpadną w niepowołane ręce, napastnicy mogą zaatakować kontrakt objęty własnością. Dlatego wykorzystanie wzorca dostępu opartego na rolach dla kilku kont administratorów może być lepszym rozwiązaniem.

W przypadku kontroli dostępu opartej na rolach dostęp do wrażliwych funkcji jest rozdysponowany pomiędzy zaufanymi uczestnikami. Na przykład jedno konto może być odpowiedzialne za mintowanie tokenów, a inne może mieć możliwość zatrzymania kontraktu lub jego aktualizacji. Decentralizowanie kontroli dostępu w taki sposób, eliminuje pojedyncze punkty awarii i ogranicza potrzebę użytkowników do powierzania zaufania.

Używanie portfelu multisig

Innym podejściem do implementacji bezpiecznej kontroli dostępu jest użycie konta z wieloma podpisami do zarządzania kontraktem. W przeciwieństwie do EOA, konta multisig mają kilku właścicieli i wymagają podpisów od określonej minimalnej liczby kont — na przykład 3 z 5 — aby wykonać transakcję.

Używanie multisig do kontroli dostępu wprowadza dodatkową warstwę bezpieczeństwa, ponieważ czynności na docelowym kontrakcie wymagają zgody kilku stron. Jest to szczególnie użyteczne, kiedy wykorzystanie wzorca własności jest niezbędne, ponieważ czyni on manipulacje wrażliwymi funkcjami kontraktu w złośliwych celach trudniejszymi dla napastnika czy wewnętrznego sabotażysty.

2. Używaj instrukcji require(), assert() i revert() do ochrony operacji kontraktowych

Jak wspomniano, każdy może wywoływać funkcje publiczne w inteligentnym kontrakcie po jego wdrożeniu w blockchainie. Ponieważ nie można z góry przewidzieć, w jaki sposób konta zewnętrzne będą oddziaływać na umowę, najlepszym rozwiązaniem jest wdrożenie wewnętrznych zabezpieczeń chroniących przed problematycznymi operacjami przed wdrożeniem. Możesz wymusić poprawne zachowanie w inteligentnych kontraktach za pomocą instrukcji require(), assert() i revert(), aby wywoływać wyjątki i cofać zmiany stanu, jeśli wykonanie nie spełni określonych wymagań.

require(): Instrukcje require są definiowane na początku funkcji i zapewniają, że określone warunki są spełnione, zanim wywołana funkcja zostanie wykonana. Instrukcja require może być użyta do walidacji danych wejściowych użytkownika, sprawdzania zmiennych stanu lub uwierzytelniania tożsamości konta wywołującego przed kontynuowaniem wykonania funkcji.

assert(): assert() służy do wykrywania błędów wewnętrznych i sprawdzania naruszeń „niezmienników” w kodzie. Niezmiennik to logiczne założenie dotyczące stanu kontraktu, które powinno być prawdziwe dla wszystkich możliwych wykonań funkcji. Przykładowym niezmiennikiem jest maksymalna podaż całkowita lub saldo w kontrakcie tokena. Użycie assert() zapewnia, że kontrakt nigdy nie osiągnie stanu podatności na ataki, a jeśli tak się stanie, wszystkie zmiany w zmiennych stanu zostaną wycofane.

revert(): revert() może być użyte w instrukcji if-else, która wywołuje wyjątek, jeśli wymagany warunek nie jest spełniony. Poniższy przykładowy kontrakt wykorzystuje revert() do ochrony wykonywania funkcji:

1pragma solidity ^0.8.4;
2
3contract VendingMachine {
4 address owner;
5 error Unauthorized();
6 function buy(uint amount) public payable {
7 if (amount > msg.value / 2 ether)
8 revert("Za mało Etheru.");
9 // Dokonaj zakupu.
10 }
11 function withdraw() public {
12 if (msg.sender != owner)
13 revert Unauthorized();
14
15 payable(msg.sender).transfer(address(this).balance);
16 }
17}
Pokaż wszystko

3. Testuj inteligentne kontrakty i weryfikuj poprawność kodu

Niezmienność kodu działającego w Wirtualnej Maszynie Ethereum oznacza, że inteligentne kontrakty wymagają wyższego poziomu oceny jakości na etapie rozwoju. Dokładne testowane kontraktu i obserwowanie go w celu wychwycenia wszelkich nieoczekiwanych rezultatów, wzmocni znacząco jego bezpieczeństwo i, w dłuższym okresie, ochroni użytkowników.

Standardową metodą jest pisanie małych testów jednostkowych, używając nieprawdziwych danych, które powinny zostać dostarczone do kontraktu przez użytkowników. Testowanie jednostkowe jest dobre do testowania funkcjonalności poszczególnych funkcji i upewniania się, że inteligentny kontrakt działa zgodnie z oczekiwaniami.

Niestety testowanie jednostkowe jest niezbyt efektywne w kwestii zwiększania poziomu zabezpieczenia inteligentnego kontraktu, kiedy stosuje się je jako jedyną metodę. Test jednostkowy może dowieźć, że funkcja działa poprawnie dla nieprawdziwych danych, ale skuteczność testów jednostkowych ogranicza się do jakości napisanych testów. To zwiększa trudność wykrycia pominiętych przypadków granicznych czy słabych punktów, których wykorzystanie może złamać zabezpieczenia inteligentnego kontraktu.

Lepszym podejściem jest połączenie testowania jednostkowego z testowaniem opartym na właściwościach, przeprowadzanym przy użyciu analizy statycznej i dynamicznej. Analiza statyczna opiera się na reprezentacjach niskiego poziomu, takich jak grafy przepływu sterowaniaopens in a new tab i abstrakcyjne drzewa składniopens in a new tab, w celu analizy osiągalnych stanów programu i ścieżek wykonania. Tymczasem techniki analizy dynamicznej, takie jak fuzzing inteligentnych kontraktówopens in a new tab, wykonują kod kontraktu z losowymi wartościami wejściowymi w celu wykrycia operacji naruszających właściwości bezpieczeństwa.

Weryfikacja formalna to kolejna technika weryfikacji właściwości bezpieczeństwa w inteligentnych kontraktach. W przeciwieństwie do zwykłego testowania weryfikacja formalna może jednoznacznie dowieźć braku obecności błędów w inteligentnym kontrakcie. Osiąga się to poprzez tworzenie formalnej specyfikacji, która określa pożądane właściwości zabezpieczeń i dowodzi, że model formalny kontraktów spełnia założenia tej specyfikacji.

4. Poproś o niezależny przegląd swojego kodu

Po przetestowaniu kontraktu dobrze jest poprosić innego programistę o sprawdzenie kodu źródłowego w celu wykrycia problemów dotyczących zabezpieczeń. Testowanie nie odkryje każdej wady inteligentnego kontraktu, ale zapewnienie niezależnej recenzji zwiększy prawdopodobieństwo wykrycia słabych punktów.

Audyty

Zlecenie audytu inteligentnego kontraktu jest jednym ze sposobów na przeprowadzenie niezależnej recenzji kodu. Audytorzy odgrywają ważną rolę w zapewnieniu bezpieczeństwa inteligentnemu kontraktowi oraz sprzyjają pozbyciu się defektów i błędów projektowych.

Nie powinno to jednak doprowadzić do sytuacji, w której audyty stanowią jedyne zastosowane rozwiązanie. Audyty inteligentnych kontraktów nie wyłapią każdego błędu i są głównie zaprojektowane, aby zapewnić dodatkowy cykl recenzji, który może pomóc w wykryciu problemów przeoczonych przez programistów podczas początkowych etapów programowania oraz testów. Powinno się również stosować do najlepszych praktyk we współpracy z audytorami, które obejmują odpowiednie dokumentowanie kodu oraz dodawanie komentarzy w celu maksymalizacji korzyści płynących z audytu inteligentnego kontraktu.

Nagrody za znalezienie błędów

Zorganizowanie programu nagród za znalezienie błędu jest kolejnym podejściem do wdrożenia zewnętrznych recenzji kodu. Nagroda za znalezienie błędu jest finansową gratyfikacją przyznawaną osobom (zwykle hakerom w białych kapeluszach), które wykryją słabe punkty aplikacji.

Gdy są odpowiednio wykorzystane, nagrody za znalezienie błędu dają członkom społeczności hakerskiej zachętę do inspekcji kodu pod kątem wad krytycznych. Przykładem z życia wziętym jest „błąd nieskończonej ilości pieniędzy”, który pozwoliłby atakującemu na stworzenie nieograniczonej ilości etheru na Optimismopens in a new tab, protokole Warstwy 2 działającym na Ethereum. Na szczęście haker w białym kapeluszu odkrył tę wadęopens in a new tab i powiadomił zespół, otrzymując przy tym dużą nagrodęopens in a new tab.

Przydatną strategią jest ustalanie wysokości nagrody w proporcji do wartości zagrożonych środków. Podejście to, określane mianem „skalowalnych nagród za błędyopens in a new tab”, zapewnia zachęty finansowe dla osób, które w sposób odpowiedzialny ujawniają luki w zabezpieczeniach, zamiast je wykorzystywać.

5. Postępuj zgodnie z najlepszymi praktykami podczas tworzenia inteligentnych kontraktów

Istnienie audytów i programów nagród za znalezienie błędu nie zwalnia z odpowiedzialności za pisanie kodu wysokiej jakości. Dobre zabezpieczenie inteligentnego kontraktu zaczyna się na etapie projektowania i programowania:

  • Przechowuj całość kodu w systemie kontroli wersji takim jak git

  • Dokonywać każdej modyfikacji kodu za pomocą żądania Pull

  • Upewnić się, że żądania Pull mają przynajmniej jednego niezależnego recenzenta, co oznacza, że jeśli samodzielnie pracujesz przy projekcie, powinieneś rozważyć kontakt z innymi programistami w celu wymiany ocen kodu

  • Używaj środowiska deweloperskiego do testowania, kompilowania i wdrażania inteligentnych kontraktów

  • Przetestuj swój kod za pomocą podstawowych narzędzi do analizy kodu, takich jak Cyfrin Aderynopens in a new tab, Mythril i Slither. W idealnych warunkach powinno się robić to przed scaleniem każdego żądania pull, a następnie porównać różnice w danych wyjściowych

  • Upewnić się, że kod kompiluje się bez błędów, oraz że kompilator Solidity nie pokazuje żadnych ostrzeżeń

  • Odpowiednio dokumentuj swój kod (używając NatSpecopens in a new tab) i opisuj szczegóły dotyczące architektury kontraktu w łatwym do zrozumienia języku. Ułatwi to innym przeprowadzenie audytu i recenzji kodu.

6. Wdróż solidne plany odzyskiwania po awarii

Projektowanie bezpiecznej kontroli dostępu, implementowanie modyfikatorów funkcji oraz inne sugestie mogą zwiększyć bezpieczeństwo inteligentnego kontraktu, ale nie mogą wykluczyć ryzyka złośliwego wykorzystania. Tworzenie bezpiecznych inteligentnych kontraktów wymaga "przygotowania się na porażkę" oraz posiadania planu zapasowego na efektywną odpowiedź wobec ataku. Odpowiedni plan odzyskiwania w przypadku katastrofy powinien zawierać następujące komponenty:

Aktualizacje kontraktów

Pomimo tego, że standardowo inteligentne kontrakty Ethereum są niezmienne, możliwe jest osiągnięcie pewnego poziomu zmienności przy użyciu wzorców aktualizacji. Aktualizacja kontraktu jest niezbędne w przypadkach, w których wada krytyczna sprawia, że kontrakt staje się bezużyteczny, a wdrożenie nowej logiki jest najbardziej wykonalną możliwością.

Mechanizmy aktualizowania kontraktu działają na różne sposoby, ale "wzorzec proxy" jest jednym z najbardziej popularnych podejść do aktualizowania inteligentnych kontraktów. Wzorce proxyopens in a new tab dzielą stan i logikę aplikacji między dwa kontrakty. Pierwszy kontrakt (nazywany „kontraktem proxy”) przechowuje zmienne stanu (np. salda użytkowników), natomiast drugi kontrakt (nazywany „kontraktem logicznym”) zawiera kod do wykonywania funkcji kontraktu.

Konta wchodzą w interakcję z kontraktem proxy, który przekazuje wszystkie wywołania funkcji do kontraktu logicznego za pomocą niskopoziomowego wywołania delegatecall()opens in a new tab. W przeciwieństwie do zwykłego wywołania wiadomości, delegatecall() zapewnia, że kod działający pod adresem kontraktu logicznego jest wykonywany w kontekście kontraktu wywołującego. Oznacza to, że kontrakt logiczny zawsze będzie zapisywał dane w pamięci masowej proxy (zamiast we własnej), a oryginalne wartości msg.sender i msg.value zostaną zachowane.

Delegowanie wywołań do kontraktu logicznego wymaga przechowywania jego adresu w pamięci kontraktu proxy. Dlatego aktualizacja logiki kontraktu jest jedynie kwestią wdrożenia kolejnego kontraktu logicznego i przechowania nowego adresu w kontrakcie proxy. Jako że kolejne wywołania kontraktu proxy są automatycznie przekierowane do nowego kontraktu logicznego, "aktualizacja" została dokonana właściwie bez modyfikacji kodu.

Więcej o aktualizowaniu kontraktów.

Zatrzymania awaryjne

Jak wspomniano wcześniej, nawet dokładne audyty i testowanie nie są w stanie odkryć dokładnie wszystkich błędów w inteligentnym kontrakcie. Jeśli luka w zabezpieczeniach ujawni się w kodzie już po wdrożeniu, załatanie jej nie jest możliwe, ponieważ nie można zmienić kodu uruchomionego pod adresem kontraktu. Ponadto, implementacja mechanizmów aktualizacji (np. wzorca proxy) mogą zająć czas (wymagają one często zgody wielu stron), co działa tylko na korzyść napastników, dając im więcej czasu na dokonanie szkód.

Opcją nuklearną jest implementacja funkcji "wyłącznika awaryjnego", która blokuje wywołania zagrożonych funkcji kontraktu. Wyłączniki awaryjne zawierają zwykle następujące komponenty:

  1. Globalną zmienną logiczną, która wykazuje czy inteligentny kontrakt jest w stanie zatrzymania czy nie. Zmienna ta jest ustawiona na false podczas konfigurowania kontraktu, ale powróci do wartości true, gdy kontrakt zostanie zatrzymany.

  2. Funkcje, które odnoszą się do zmiennej logicznej w swoim działaniu. Takie funkcje są dostępne, kiedy inteligentny kontrakt nie jest zatrzymany, a przestają być dostępne, kiedy funkcja awaryjnego wyłącznika zostaje aktywowana.

  3. Podmiot, który ma dostęp do funkcji zatrzymania awaryjnego, która ustawia zmienną logiczną na true. Aby uniknąć złośliwych działań, wywołania tej funkcji mogą zostać ograniczone do zaufanych adresów (np. właściciela kontraktu).

Kiedy kontrakt aktywuje wyłącznik awaryjny, określone funkcje przestaną być dostępne. Osiąga się to poprzez opakowanie wybranych funkcji modyfikatorem, który odnosi się do zmiennej globalnej. Poniżej znajduje się przykładopens in a new tab opisujący implementację tego wzorca w kontraktach:

1// Ten kod nie został profesjonalnie zaudytowany i nie daje żadnych gwarancji bezpieczeństwa ani poprawności. Używaj na własne ryzyko.
2
3contract EmergencyStop {
4
5 bool isStopped = false;
6
7 modifier stoppedInEmergency {
8 require(!isStopped);
9 _;
10 }
11
12 modifier onlyWhenStopped {
13 require(isStopped);
14 _;
15 }
16
17 modifier onlyAuthorized {
18 // Tutaj sprawdź autoryzację msg.sender
19 _;
20 }
21
22 function stopContract() public onlyAuthorized {
23 isStopped = true;
24 }
25
26 function resumeContract() public onlyAuthorized {
27 isStopped = false;
28 }
29
30 function deposit() public payable stoppedInEmergency {
31 // Tutaj odbywa się logika depozytu
32 }
33
34 function emergencyWithdraw() public onlyWhenStopped {
35 // Tutaj odbywa się awaryjna wypłata
36 }
37}
Pokaż wszystko

Ten przykład ukazuje podstawowe cechy wyłączników awaryjnych:

  • isStopped to zmienna logiczna, która na początku ma wartość false, a po przejściu kontraktu w tryb awaryjny – true.

  • Modyfikatory funkcji onlyWhenStopped i stoppedInEmergency sprawdzają zmienną isStopped. stoppedInEmergency służy do kontrolowania funkcji, które powinny być niedostępne, gdy kontrakt jest podatny na ataki (np. deposit()). Wywołania tej funkcji po prostu zostaną anulowane.

onlyWhenStopped jest używany dla funkcji, które powinny być możliwe do wywołania w sytuacji awaryjnej (np. emergencyWithdraw()). Takie funkcje mogą pomóc w rozwiązaniu sytuacji, dlatego wykluczone są z listy "zastrzeżonych funkcji".

Użycie funkcji wyłącznika awaryjnego zapewnia efektywne rozwiązanie tymczasowe pozwalające poradzić sobie z poważnymi podatnościami inteligentnego kontraktu. Zwiększa jednak potrzebę zaufania wobec programistów, że nie użyją jej dla własnej korzyści. Aby się to tego odnieść, można użyć środków takich jak decentralizacja kontroli nad wyłącznikami awaryjnymi poprzez poddanie jej pod mechanizm głosowania onchain, zamku czasowego lub użycia do jej kontroli portfela multisig.

Monitorowanie zdarzeń

Zdarzeniaopens in a new tab pozwalają śledzić wywołania funkcji inteligentnych kontraktów i monitorować zmiany zmiennych stanu. Zaleca się programowanie kontraktów w taki sposób, aby emitowały zdarzenie za każdym razem, gdy jakiś podmiot wykonuje czynność istotną z perspektywy zabezpieczeń (np. wypłatę środków).

Rejestrowanie zdarzeń i monitorowanie ich offchain zapewnia wgląd do operacji kontraktu i pomaga w szybszym odkrywaniu złośliwych czynności. Oznacza to, że zespół może szybciej odpowiedzieć na haki i zacząć działać, aby ograniczyć negatywny wpływ grożący użytkownikom, np. zatrzymując funkcje lub wykonując aktualizację.

Można również zdecydować się na gotowe narzędzie do monitorowania, które automatycznie przekazuje ostrzeżenia za każdym razem, kiedy ktoś wchodzi w interakcję z kontraktem. Narzędzia te pozwalają stworzyć niestandardowe powiadomienia wywoływane różnymi wyzwalaczami takimi jak wielkość wolumenu transakcji, częstotliwość wywoływania funkcji oraz użycie określonych funkcji. Na przykład można zaprogramować powiadomienie, który przychodzi, kiedy suma wypłacona w jednej transakcji przekracza określony próg.

7. Projektuj bezpieczne systemy zarządzania

Warte rozważenia jest zdecentralizowanie aplikacji poprzez oddanie kontroli nad kluczowymi inteligentnymi kontraktami członkom społeczności. W tym przypadku system inteligentnego kontraktu będzie zawierał moduł zarządzający — mechanizm, który pozwala członkom społeczności aprobować czynności administracyjne poprzez system zarządzania onchain. Na przykład propozycja aktualizacji kontraktu proxy do nowej wersji może być poddana głosowaniu posiadaczom tokena.

Zdecentralizowane zarządzanie może być korzystne, ponieważ czyni zbieżnym interes programistów oraz użytkowników końcowych. Niemniej mechanizmy zarządzania inteligentnymi kontraktami mogą wprowadzić nowe ryzyka w przypadku niepoprawnej implementacji. Prawdopodobnym scenariuszem jest sytuacja, w której atakujący zdobywa ogromną siłę głosu (mierzoną liczbą posiadanych tokenów) poprzez wzięcie błyskawicznej pożyczki i przeforsowuje złośliwą propozycję.

Jednym ze sposobów zapobiegania problemom związanym z zarządzaniem on-chain jest użycie blokady czasowej (timelock)opens in a new tab. Zamek czasowy powstrzymuje inteligentny kontrakt przed wykonaniem pewnej funkcji, zanim minie określona ilość czasu. Inne strategie przewidują nadanie "ciężaru głosu" każdemu tokenowi na bazie czasu, na jaki został zablokowany lub zmierzenie siły adresu w przeszłym okresie (na przykład 2 czy 3 bloki wstecz) zamiast w czasie aktualnego bloku. Obie metody obniżają ryzyko szybkiego gromadzenia siły głosu, aby przechylić na swoją korzyść głosowania onchain.

Więcej na temat projektowania bezpiecznych systemów zarządzaniaopens in a new tab, różnych mechanizmów głosowania w DAOopens in a new tab oraz powszechnych wektorów ataków na DAO wykorzystujących DeFiopens in a new tab w udostępnionych linkach.

8. Ogranicz złożoność kodu do minimum

Programiści tradycyjnego oprogramowania są zaznajomieni z zasadą KISS ("ma być prosto głupku"), która odradza wprowadzania niepotrzebnej złożoności do projektu oprogramowania. Jest to zgodne ze znanym od dawana stwierdzeniem, że "złożone systemy zawodzą w złożone sposoby" oraz są najbardziej podatne na kosztowne błędy.

Zachowanie prostoty jest szczególnie ważne przy pisaniu inteligentnych kontraktów, jeśli weźmiemy pod wagę fakt, że inteligentne kontrakty mogą potencjalnie kontrolować aktywa o wysokiej wartości. Wskazówką pozwalającą na osiągnięcie prostoty podczas pisania inteligentnych kontraktów jest ponowne wykorzystywanie istniejących bibliotek, takich jak OpenZeppelin Contractsopens in a new tab, tam, gdzie to możliwe. Biblioteki te bowiem poddane były dokładnym audytom oraz testom prowadzonym przez programistów. Używanie ich ogranicza ryzyko popełnienia błędu podczas pisania nowych funkcji od zera.

Inną powszechną wskazówką jest pisanie niewielkich funkcji i dbanie o modularność kontraktów poprzez rozdzielanie logiki biznesowej pomiędzy wiele kontraktów. Pisanie prostszego kodu nie tylko ogranicza powierzchnię potencjalnego ataku w inteligentnym kontrakcie, ale również ułatwia rozważania na temat poprawności całego systemu oraz szybkie wykrywanie możliwych błędów projektowych.

9. Chroń się przed powszechnymi lukami w zabezpieczeniach inteligentnych kontraktów

Ponowne wejście (reentrancy)

EVM nie pozwala na współbieżność w takim znaczeniu, że dwa kontrakty biorące udział w wywołaniu komunikatu nie mogą działać jednocześnie. Zewnętrzne wywołanie zatrzymuje działanie kontraktu wywołującego oraz jego pamięć do czasu powrotu wywołania. Po tym fakcie działanie jest wznowione. Proces ten można formalnie opisać jako przekazanie przepływu sterowaniaopens in a new tab do innego kontraktu.

W większości przypadków jest to nieszkodliwe, ale przenoszenie kontroli przepływu do niezaufanych kontaktów może powodować problemy takie jak atak rekurencyjny. Atak rekurencyjny występuje, gdy złośliwy kontrakt powraca wywołanie do narażonego kontraktu, zanim wywołanie właściwej funkcji dobiegnie końca. Najlepiej wyjaśnić to za pomocą przykładu.

Weźmy prosty inteligentny kontrakt ("Ofiarę"), który pozwala każdemu na deponowanie i wypłacanie eteru:

1// Ten kontrakt jest podatny na ataki. Nie używać w środowisku produkcyjnym
2
3contract Victim {
4 mapping (address => uint256) public balances;
5
6 function deposit() external payable {
7 balances[msg.sender] += msg.value;
8 }
9
10 function withdraw() external {
11 uint256 amount = balances[msg.sender];
12 (bool success, ) = msg.sender.call.value(amount)("");
13 require(success);
14 balances[msg.sender] = 0;
15 }
16}
Pokaż wszystko

Ten kontrakt udostępnia funkcję withdraw(), aby umożliwić użytkownikom wypłatę ETH wcześniej zdeponowanego w kontrakcie. Przetwarzając wypłatę, kontrakt dokonuje następujących działań:

  1. Sprawdza saldo ETH użytkownika
  2. Wysyła środki na wywołujący adres
  3. Resetuje ich saldo do 0 uniemożliwiając dalszych wypłat temu użytkownikowi

Funkcja withdraw() w kontrakcie Victim jest zgodna ze wzorcem „sprawdzenie-interakcje-efekty”. Sprawdza, czy warunki niezbędne do wykonania są spełnione (tj. użytkownik ma dodatnie saldo ETH) i wykonuje interakcję, wysyłając ETH na adres wywołującego, przed zastosowaniem efektów transakcji (tj. zmniejszeniem salda użytkownika).

Jeśli funkcja withdraw() jest wywoływana z konta zewnętrznego (EOA), funkcja wykonuje się zgodnie z oczekiwaniami: msg.sender.call.value() wysyła ETH do wywołującego. Jeśli jednak msg.sender jest kontem inteligentnego kontraktu, które wywołuje withdraw(), wysłanie środków za pomocą msg.sender.call.value() spowoduje również uruchomienie kodu zapisanego pod tym adresem.

Wyobraź sobie, że to jest kod wdrożony na adresie kontraktu:

1 contract Attacker {
2 function beginAttack() external payable {
3 Victim(victim_address).deposit.value(1 ether)();
4 Victim(victim_address).withdraw();
5 }
6
7 function() external payable {
8 if (gasleft() > 40000) {
9 Victim(victim_address).withdraw();
10 }
11 }
12}
Pokaż wszystko

Ten kontrakt jest zaprojektowany, aby wykonywać trzy rzeczy:

  1. Akceptować depozyt od innego konta (prawdopodobnie od EOA napastnika)
  2. Dokonać depozytu 1 ETH na kontrakt Ofiary
  3. Wypłacić ten 1 ETH przechowywany przez inteligentny kontrakt

Nie ma w tym nic złego, z wyjątkiem tego, że Attacker ma inną funkcję, która ponownie wywołuje withdraw() w Victim, jeśli ilość gazu pozostała z przychodzącego msg.sender.call.value jest większa niż 40 000. Daje to Attacker możliwość ponownego wejścia do Victim i wypłacenia większej ilości środków, zanim pierwsze wywołanie withdraw zostanie zakończone. Cykl wygląda następująco:

1- EOA atakującego wywołuje `Attacker.beginAttack()` z 1 ETH
2- `Attacker.beginAttack()` wpłaca 1 ETH do `Victim`
3- `Attacker` wywołuje `withdraw()` w `Victim`
4- `Victim` sprawdza saldo `Attacker` (1 ETH)
5- `Victim` wysyła 1 ETH do `Attacker` (co uruchamia funkcję domyślną)
6- `Attacker` ponownie wywołuje `Victim.withdraw()` (zauważ, że `Victim` nie zmniejszyło salda `Attacker` po pierwszej wypłacie)
7- `Victim` sprawdza saldo `Attacker` (które wciąż wynosi 1 ETH, ponieważ nie zastosowano efektów pierwszego wywołania)
8- `Victim` wysyła 1 ETH do `Attacker` (co uruchamia funkcję domyślną i pozwala `Attacker` na ponowne wejście do funkcji `withdraw`)
9- Proces powtarza się, dopóki `Attacker` nie wyczerpie gazu, w którym to momencie `msg.sender.call.value` zwraca wartość bez uruchamiania dodatkowych wypłat
10- `Victim` w końcu stosuje wyniki pierwszej (i kolejnych) transakcji do swojego stanu, więc saldo `Attacker` jest ustawiane na 0
Pokaż wszystko

Podsumowując, z uwagi na to, że saldo wywołującego nie jest ustawione na 0 dopóki wykonanie funkcji nie jest skończone, późniejsze wywołania osiągną sukces i pozwolą wywołującemu wypłacić środki wielokrotnie. Ten rodzaj ataku może być wykorzystany do drenażu środków z inteligentnego kontraktu, jak to miało miejsce podczas ataku na DAO w 2016 rokuopens in a new tab. Ataki typu reentrancy są do dziś krytycznym problemem dla inteligentnych kontraktów, o czym świadczą publiczne listy exploitów reentrancyopens in a new tab.

Jak zapobiegać atakom rekurencyjnym

Jednym ze sposobów radzenia sobie z ponownym wejściem jest przestrzeganie wzorca sprawdzenie-efekty-interakcjeopens in a new tab. Ten wzorzec kieruje wykonaniem funkcji w taki sposób, że w pierwszej kolejności dokonuje niezbędnych sprawdzeń, następnie wykonuje kod zmieniający stan kontraktu, a na końcu kod, który wchodzi w interakcję z kontraktami lub EOA.

Wzorzec sprawdzenie-efekty-interakcje jest użyty w poprawionej wersji kontraktu Victim pokazanej poniżej:

1contract NoLongerAVictim {
2 function withdraw() external {
3 uint256 amount = balances[msg.sender];
4 balances[msg.sender] = 0;
5 (bool success, ) = msg.sender.call.value(amount)("");
6 require(success);
7 }
8}

Ten kontrakt wykonuje sprawdzenie salda użytkownika, stosuje efekty funkcji withdraw() (zerując saldo użytkownika), a następnie przechodzi do wykonania interakcji (wysłania ETH na adres użytkownika). Dzięki temu kontrakt aktualizuje swoją pamięć przed zewnętrznym wywołaniem, eliminując tym samym warunki do ataku rekurencyjnego, które zaistniały wcześniej. Kontrakt Attacker wciąż może wywołać NoLongerAVictim, ale ponieważ balances[msg.sender] zostało ustawione na 0, dodatkowe wypłaty spowodują błąd.

Innym rozwiązaniem jest zastosowanie wzajemnie wykluczających się zamków (powszechnie znanych pod nazwą "mutex"), które zamykają część stanu kontraktu do czasu zakończenia wywołania funkcji. Jest to realizowane za pomocą zmiennej logicznej, która jest ustawiana na true przed wykonaniem funkcji i wraca do false po zakończeniu wywołania. Jak pokazuje poniższy przykład, używanie mutex zapewnia ochronę przed atakami rekurencyjnymi w czasie, kiedy pierwotne wywołanie wciąż jest przetwarzane.

1pragma solidity ^0.7.0;
2
3contract MutexPattern {
4 bool locked = false;
5 mapping(address => uint256) public balances;
6
7 modifier noReentrancy() {
8 require(!locked, "Zablokowano przed ponownym wejściem.");
9 locked = true;
10 _;
11 locked = false;
12 }
13 // Ta funkcja jest chroniona przez muteks, więc ponowne wywołania z wewnątrz `msg.sender.call` nie mogą ponownie wywołać `withdraw`.
14 // Instrukcja `return` zwraca `true`, ale nadal ocenia instrukcję `locked = false` w modyfikatorze
15 function withdraw(uint _amount) public payable noReentrancy returns(bool) {
16 require(balances[msg.sender] >= _amount, "Brak salda do wypłaty.");
17
18 balances[msg.sender] -= _amount;
19 (bool success, ) = msg.sender.call{value: _amount}("");
20 require(success);
21
22 return true;
23 }
24}
Pokaż wszystko

Można również użyć systemu płatności typu pullopens in a new tab, który wymaga od użytkowników wypłacania środków z inteligentnych kontraktów, zamiast systemu „płatności typu push”, który wysyła środki na konta. Eliminuje to możliwość przypadkowego uruchomienia kodu pod nieznanymi adresami (i może również zapobiec niektórym atakom typu „odmowa usługi”).

Niedomiar i przepełnienie liczb całkowitych

Przepełnienie całkowite występuje, gdy wyniki operacji arytmetycznej wykroczą poza dopuszczalny zakres wartości, powodując ich „przesunięcie” do najniższej możliwej do przedstawienia wartości. Na przykład uint8 może przechowywać wartości tylko do 2^8-1=255. Operacje arytmetyczne, których wynik jest większy niż 255, spowodują przepełnienie i zresetowanie uint do 0, podobnie jak licznik kilometrów w samochodzie zeruje się po osiągnięciu maksymalnego przebiegu (999999).

Niedomiar liczb całkowitych zdarza się z podobnych powodów: wynik operacji arytmetycznej mieści się poniżej dopuszczalnego zakresu. Powiedzmy, że próbujesz dekrementować 0 w uint8, wynik po prostu „przewinie się” do maksymalnej reprezentowalnej wartości (255).

Zarówno przepełnienia, jak i niedopełnienia liczb całkowitych mogą prowadzić do nieoczekiwanych zmian zmiennych stanu kontraktu i skutkować nieplanowanym wykonaniem. Poniżej znajduje się przykład pokazujący, w jaki sposób atakujący może wykorzystać przepełnienie arytmetyczne w inteligentnym kontrakcie do wykonania nieprawidłowej operacji:

1pragma solidity ^0.7.6;
2
3// Ten kontrakt ma działać jako skarbiec czasowy.
4// Użytkownik może wpłacać środki do tego kontraktu, ale nie może ich wypłacić przez co najmniej tydzień.
5// Użytkownik może również wydłużyć czas oczekiwania poza 1-tygodniowy okres.
6
7/*
81. Wdróż TimeLock
92. Wdróż Attack z adresem TimeLock
103. Wywołaj Attack.attack wysyłając 1 ether. Natychmiast będziesz mógł
11 wypłacić swój ether.
12
13Co się stało?
14Atak spowodował przepełnienie TimeLock.lockTime i umożliwił wypłatę
15przed upływem 1-tygodniowego okresu oczekiwania.
16*/
17
18contract TimeLock {
19 mapping(address => uint) public balances;
20 mapping(address => uint) public lockTime;
21
22 function deposit() external payable {
23 balances[msg.sender] += msg.value;
24 lockTime[msg.sender] = block.timestamp + 1 weeks;
25 }
26
27 function increaseLockTime(uint _secondsToIncrease) public {
28 lockTime[msg.sender] += _secondsToIncrease;
29 }
30
31 function withdraw() public {
32 require(balances[msg.sender] > 0, "Niewystarczające środki");
33 require(block.timestamp > lockTime[msg.sender], "Czas blokady nie upłynął");
34
35 uint amount = balances[msg.sender];
36 balances[msg.sender] = 0;
37
38 (bool sent, ) = msg.sender.call{value: amount}("");
39 require(sent, "Nie udało się wysłać Etheru");
40 }
41}
42
43contract Attack {
44 TimeLock timeLock;
45
46 constructor(TimeLock _timeLock) {
47 timeLock = TimeLock(_timeLock);
48 }
49
50 fallback() external payable {}
51
52 function attack() public payable {
53 timeLock.deposit{value: msg.value}();
54 /*
55 jeśli t = bieżący czas blokady, musimy znaleźć x takie, że
56 x + t = 2**256 = 0
57 więc x = -t
58 2**256 = type(uint).max + 1
59 więc x = type(uint).max + 1 - t
60 */
61 timeLock.increaseLockTime(
62 type(uint).max + 1 - timeLock.lockTime(address(this))
63 );
64 timeLock.withdraw();
65 }
66}
Pokaż wszystko
Jak zapobiegać niedomiarom i przepełnieniom całkowitym

Od wersji 0.8.0 kompilator Solidity odrzuca kod powodujący przepełnienia i niedopełnienia liczb całkowitych. Jednakże kontrakty skompilowane przy użyciu niższej wersji kompilatora powinny albo wykonywać sprawdzenia funkcji obejmujących operacje arytmetyczne, albo używać biblioteki (np. SafeMathopens in a new tab), która sprawdza niedomiar/przepełnienie.

Manipulacja wyroczniami

Wyrocznie pozyskują informacje spoza łańcucha (off-chain) i przesyłają je do łańcucha (on-chain) do wykorzystania przez inteligentne kontrakty. Dzięki wyroczniom można projektować inteligentne kontrakty, które współpracują z systemami off-chain, takimi jak rynki kapitałowe, co znacznie rozszerza ich zastosowanie.

Jeśli jednak wyrocznia jest uszkodzona i wysyła nieprawidłowe informacje w łańcuchu, inteligentne kontrakty będą wykonywane w oparciu o błędne dane wejściowe, co może powodować problemy. To jest podstawa „problemu wyroczni”, który dotyczy zadania upewnienia się, że informacje z wyroczni blockchain są dokładne, aktualne i aktualne.

Podobnym problemem bezpieczeństwa jest korzystanie z wyroczni łańcuchowej, takiej jak zdecentralizowana giełda, w celu uzyskania bieżącej ceny aktywów. Platformy pożyczkowe w branży zdecentralizowanych finansów (DeFi) często robią to, aby określić wartość zabezpieczenia użytkownika w celu ustalenia, ile może on pożyczyć.

Ceny DEX są często dokładne, głównie dzięki arbitrażystom przywracającym parytet na rynkach. Są one jednak podatne na manipulację, szczególnie jeśli wyrocznia on-chain oblicza ceny aktywów w oparciu o historyczne wzorce handlowe (co zwykle ma miejsce).

Przykładowo, atakujący może sztucznie zawyżać cenę spot aktywów, zaciągając pożyczkę błyskawiczną tuż przed podpisaniem umowy kredytowej. Zapytanie o cenę aktywów na giełdzie DEX zwróciłoby wyższą niż zwykle wartość (z powodu dużego „zlecenia kupna” atakującego, zaburzającego popyt na aktywa), co umożliwiłoby mu pożyczenie większej kwoty, niż powinien. Tego typu „błyskawiczne ataki kredytowe” wykorzystywano w celu wykorzystania zależności aplikacji DeFi od wyroczni cenowych, co kosztowało protokoły miliony dolarów w postaci utraconych środków.

Jak zapobiegać manipulacjom wyrocznią

Minimalnym wymogiem, aby uniknąć manipulacji wyroczniąopens in a new tab, jest korzystanie ze zdecentralizowanej sieci wyroczni, która pobiera informacje z wielu źródeł, aby uniknąć pojedynczych punktów awarii. W większości przypadków zdecentralizowane wyrocznie mają wbudowane zachęty kryptoekonomiczne, które mają skłaniać węzły wyroczni do raportowania prawidłowych informacji, dzięki czemu są bezpieczniejsze niż wyrocznie scentralizowane.

Jeśli planujesz wysłać zapytanie do wyroczni onchain o ceny aktywów, rozważ użycie takiej, która implementuje mechanizm średniej ceny ważonej w czasie (TWAP). Wyrocznia TWAPopens in a new tab wysyła zapytanie o cenę aktywa w dwóch różnych punktach w czasie (które można modyfikować) i oblicza cenę spot na podstawie uzyskanej średniej. Wybór dłuższych okresów czasu chroni protokół przed manipulacją cenami, ponieważ duże zlecenia zrealizowane niedawno nie mają wpływu na ceny aktywów.

Zasoby dotyczące bezpieczeństwa inteligentnych kontraktów dla programistów

Narzędzia do analizy inteligentnych kontraktów i weryfikacji poprawności kodu

  • Narzędzia i biblioteki testoweZbiór standardowych narzędzi i bibliotek do przeprowadzania testów jednostkowych oraz analizy statycznej i dynamicznej inteligentnych kontraktów.

  • Narzędzia do weryfikacji formalnejnarzędzia do weryfikacji poprawności funkcjonalnej w inteligentnych kontraktach i sprawdzania niezmienników.

  • Usługi audytu inteligentnych kontraktówLista organizacji świadczących usługi audytu inteligentnych kontraktów dla projektów deweloperskich Ethereum.

  • Platformy nagród za błędyPlatformy do koordynowania nagród za błędy i nagradzania za odpowiedzialne ujawnianie krytycznych luk w zabezpieczeniach inteligentnych kontraktów.

  • Fork Checkeropens in a new tabDarmowe narzędzie online do sprawdzania wszystkich dostępnych informacji dotyczących sforkowanego kontraktu.

  • ABI Encoderopens in a new tabdarmowa usługa online do kodowania funkcji kontraktu Solidity i argumentów konstruktora.

  • Aderynopens in a new tabStatyczny analizator Solidity, przechodzący przez Abstrakcyjne Drzewa Składni (AST) w celu wskazania podejrzanych luk w zabezpieczeniach i drukowania problemów w łatwym do przyswojenia formacie markdown.

Narzędzia do monitorowania inteligentnych kontraktów

  • Tenderly Real-Time Alertingopens in a new tabNarzędzie do otrzymywania powiadomień w czasie rzeczywistym, gdy na Twoich inteligentnych kontraktach lub w portfelach zdarzają się nietypowe lub nieoczekiwane zdarzenia.

Narzędzia do bezpiecznego administrowania inteligentnymi kontraktami

  • Safeopens in a new tabportfel inteligentnego kontraktu działający na Ethereum, który wymaga minimalnej liczby osób do zatwierdzenia transakcji, zanim będzie mogła ona nastąpić (M-z-N).

  • OpenZeppelin Contractsopens in a new tabBiblioteki kontraktów do wdrażania funkcji administracyjnych, w tym własności kontraktu, uaktualnień, kontroli dostępu, zarządzania, możliwości wstrzymania i innych.

Usługi audytu inteligentnych kontraktów

  • ConsenSys Diligenceopens in a new tabUsługa audytu inteligentnych kontraktów, która pomaga projektom w całym ekosystemie blockchain upewnić się, że ich protokoły są gotowe do uruchomienia i zbudowane w celu ochrony użytkowników.

  • CertiKopens in a new tabFirma zajmująca się bezpieczeństwem blockchain, pionier w stosowaniu najnowocześniejszej technologii weryfikacji formalnej w inteligentnych kontraktach i sieciach blockchain.

  • Trail of Bitsopens in a new tabFirma zajmująca się cyberbezpieczeństwem, która łączy badania nad bezpieczeństwem z mentalnością atakującego, aby zmniejszyć ryzyko i wzmocnić kod.

  • PeckShieldopens in a new tabFirma zajmująca się bezpieczeństwem blockchain, oferująca produkty i usługi zapewniające bezpieczeństwo, prywatność i użyteczność całego ekosystemu blockchain.

  • QuantStampopens in a new tabUsługa audytorska ułatwiająca powszechne przyjęcie technologii blockchain poprzez usługi oceny bezpieczeństwa i ryzyka.

  • OpenZeppelinopens in a new tabFirma zajmująca się bezpieczeństwem inteligentnych kontraktów, która przeprowadza audyty bezpieczeństwa dla systemów rozproszonych.

  • Runtime Verificationopens in a new tabFirma zajmująca się bezpieczeństwem, specjalizująca się w formalnym modelowaniu i weryfikacji inteligentnych kontraktów.

  • Hackenopens in a new tabAudytor cyberbezpieczeństwa Web3, który wprowadza kompleksowe podejście (360 stopni) do bezpieczeństwa blockchain.

  • Nethermindopens in a new tabUsługi audytu Solidity i Cairo, zapewniające integralność inteligentnych kontraktów i bezpieczeństwo użytkowników w sieciach Ethereum i Starknet.

  • HashExopens in a new tabHashEx koncentruje się na audycie blockchain i inteligentnych kontraktów w celu zapewnienia bezpieczeństwa kryptowalut, świadcząc usługi takie jak tworzenie inteligentnych kontraktów, testy penetracyjne, doradztwo w zakresie blockchain.

  • Code4renaopens in a new tabKonkurencyjna platforma audytowa, która zachęca ekspertów ds. bezpieczeństwa inteligentnych kontraktów do znajdowania luk w zabezpieczeniach i pomaga uczynić web3 bezpieczniejszym.

  • CodeHawksopens in a new tabKonkurencyjna platforma audytowa organizująca konkursy audytu inteligentnych kontraktów dla badaczy bezpieczeństwa.

  • Cyfrinopens in a new tabpotęga bezpieczeństwa Web3, inkubująca bezpieczeństwo kryptowalut poprzez produkty i usługi audytu inteligentnych kontraktów.

  • ImmuneBytesopens in a new tabFirma zajmująca się bezpieczeństwem Web3, oferująca audyty bezpieczeństwa systemów blockchain za pośrednictwem zespołu doświadczonych audytorów i najlepszych w swojej klasie narzędzi.

  • Oxorioopens in a new tabAudyty inteligentnych kontraktów i usługi bezpieczeństwa blockchain z doświadczeniem w technologiach EVM, Solidity, ZK, Cross-chain dla firm kryptograficznych i projektów DeFi.

  • Inferenceopens in a new tabFirma audytorska w zakresie bezpieczeństwa, specjalizująca się w audycie inteligentnych kontraktów dla blockchainów opartych na EVM. Eksperci w dziedzinie audytów umiejący zidentyfikować potencjalne problemy i zasugerować działające rozwiązania do zastosowania przed wdrożeniem._

Platformy bug bounty

  • Immunefiopens in a new tabPlatforma bug bounty dla inteligentnych kontraktów i projektów DeFi, na której badacze bezpieczeństwa przeglądają kod, ujawniają luki w zabezpieczeniach, otrzymują wynagrodzenie i sprawiają, że kryptowaluty są bezpieczniejsze.

  • HackerOneopens in a new tabPlatforma koordynacji luk w zabezpieczeniach i nagród za błędy, która łączy firmy z testerami penetracyjnymi i badaczami cyberbezpieczeństwa.

  • HackenProofopens in a new tabEkspercka platforma nagród za błędy dla projektów kryptograficznych (DeFi, inteligentne kontrakty, portfele, CEX i inne), na której specjaliści ds. bezpieczeństwa świadczą usługi selekcji, a badacze otrzymują wynagrodzenie za odpowiednie, zweryfikowane raporty o błędach.

  • Sherlockopens in a new tabGwarant w Web3 w zakresie bezpieczeństwa inteligentnych kontraktów, z wypłatami dla audytorów zarządzanymi za pośrednictwem inteligentnych kontraktów, aby zapewnić, że odpowiednie błędy są uczciwie opłacane.

  • CodeHawksopens in a new tabKonkurencyjna platforma bug bounty, na której audytorzy biorą udział w konkursach i wyzwaniach dotyczących bezpieczeństwa, a (wkrótce) także we własnych prywatnych audytach.

Publikacje znanych luk w zabezpieczeniach inteligentnych kontraktów i exploitów

Wyzwania związane z nauką bezpieczeństwa inteligentnych kontraktów

Najlepsze praktyki w zakresie zabezpieczania inteligentnych kontraktów

Samouczki dotyczące bezpieczeństwa inteligentnych kontraktów

Czy ten artykuł był pomocny?