Najlepsze praktyki bezpieczeństwa kontraktów
Poznaj kluczowe praktyki bezpieczeństwa podczas tworzenia smart kontraktów, w tym zabezpieczenia przed reentrancy, wzorzec checks-effects-interactions i kontrolę dostępu.
Najlepsze praktyki bezpieczeństwa kontraktów to bezpłatna lekcja Web3 & DApp Development Fundamentals na CoddyKit. To lekcja 2 z 3. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Web3 & DApp Development Fundamentals, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Web3 & DApp Development Fundamentals zawiera 3 lekcji w sumie.
Wprowadzenie do bezpieczeństwa smart kontraktów
Witamy w niezwykle istotnym świecie bezpieczeństwa smart kontraktów! W przeciwieństwie do tradycyjnego oprogramowania błędy w smart kontraktach mogą prowadzić do nieodwracalnej utraty środków.
Ponieważ po wdrożeniu smart kontrakty są niezmienne, naprawianie luk w zabezpieczeniach jest niezwykle trudne i często wymaga złożonych mechanizmów aktualizacji, a nawet ponownego wdrożenia nowego kontraktu.
W tej lekcji omówimy najważniejsze praktyki pozwalające tworzyć bezpieczniejsze i bardziej niezawodne smart kontrakty.
Zrozumienie reentrancy
Jedną z najbardziej znanych luk w zabezpieczeniach jest reentrancy. Występuje ona, gdy zewnętrzne wywołanie innego kontraktu lub adresu ponownie „wchodzi” do wywołującego kontraktu, zanim zakończy się aktualizacja stanu początkowej funkcji.
Wyobraź sobie bankomat, który pozwala wypłacać pieniądze. Jeśli obciąża konto *po* wydaniu gotówki, atak reentrancy przypominałby wielokrotne proszenie o gotówkę, zanim system zaktualizuje saldo, co doprowadziłoby do opróżnienia bankomatu.
Podatny kod wypłaty
Rozważmy ten uproszczony kontrakt, w którym użytkownik może wpłacać i wypłacać Ether. Czy dostrzega Pan/Pani zagrożenie?
Funkcja withdraw() najpierw wysyła Ether, a dopiero potem aktualizuje saldo. Atakujący może ponownie wywołać withdraw() ze swojego złośliwego kontraktu podczas zewnętrznego wywołania, zanim jego saldo zostanie wyzerowane.
/// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract VulnerableBank {
mapping(address => uint) public balances;
function deposit() public payable {
balances[msg.sender] += msg.value;
}
function withdraw(uint _amount) public {
require(balances[msg.sender] >= _amount, "Insufficient balance");
// Vulnerable point: send Ether BEFORE updating balance
(bool success, ) = msg.sender.call{value: _amount}("");
require(success, "Failed to send Ether");
balances[msg.sender] -= _amount; // This happens AFTER the external call
}
function getBalance() public view returns (uint) {
return address(this).balance;
}
}Zapobieganie reentrancy: blokady stanu
Typowym i skutecznym sposobem zapobiegania reentrancy jest użycie strażnika przed reentrancy. Polega to na zablokowaniu stanu kontraktu podczas zewnętrznego wywołania i odblokowaniu go po jego zakończeniu.
Jeśli ponowne wywołanie spróbuje wykonać zablokowaną funkcję, zostanie ono wycofane. ReentrancyGuard firmy OpenZeppelin to popularna implementacja, ale można również utworzyć prostą implementację samodzielnie.
Implementacja strażnika przed reentrancy
Dodajmy prostego strażnika przed reentrancy, używając flagi logicznej. Dzięki temu funkcja withdraw nie będzie mogła zostać wywołana ponownie do czasu zakończenia bieżącego wykonania i zaktualizowania stanu.
Modyfikator nonReentrant ustawia blokadę, wykonuje operację, a następnie ją zwalnia.
/// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract SecureBank {
mapping(address => uint) public balances;
bool private _locked; // Reentrancy guard flag
modifier nonReentrant() {
require(!_locked, "Reentrant call detected");
_locked = true;
_;
_locked = false;
}
function deposit() public payable {
balances[msg.sender] += msg.value;
}
function withdraw(uint _amount) public nonReentrant {
require(balances[msg.sender] >= _amount, "Insufficient balance");
balances[msg.sender] -= _amount; // Update balance BEFORE external call (CEI)
(bool success, ) = msg.sender.call{value: _amount}("");
require(success, "Failed to send Ether");
}
function getBalance() public view returns (uint) {
return address(this).balance;
}
}Wzorzec CEI
Wzorzec Checks-Effects-Interactions (CEI) to podstawowa praktyka bezpieczeństwa. Określa on konkretną kolejność operacji w funkcji:
- Checks: Sprawdzanie warunków (np. instrukcji
requireionlyOwner). - Effects: Aktualizowanie stanu kontraktu (np.
balances[msg.sender] -= amount). - Interactions: Wykonywanie zewnętrznych wywołań innych kontraktów lub adresów.
Stosowanie CEI pomaga zapobiegać różnym atakom, w tym reentrancy, ponieważ gwarantuje zakończenie aktualizacji stanu kontraktu *przed* zewnętrznymi wywołaniami.
CEI w funkcji wypłaty
Zwróć uwagę, że funkcja withdraw naszego kontraktu SecureBank już stosuje wzorzec CEI:
- Checks:
require(balances[msg.sender] >= _amount, ...)orazrequire(!_locked, ...)z modyfikatora. - Effects:
balances[msg.sender] -= _amount;aktualizuje stan. - Interactions:
msg.sender.call{value: _amount}("");wykonuje zewnętrzny transfer.
Ta kolejność ma kluczowe znaczenie dla zapobiegania reentrancy i zapewnienia spójności stanu.
/// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract CEIDemo {
mapping(address => uint) public balances;
bool private _locked;
modifier nonReentrant() {
require(!_locked, "Reentrant call detected");
_locked = true;
_;
_locked = false;
}
function deposit() public payable {
balances[msg.sender] += msg.value;
}
function withdraw(uint _amount) public nonReentrant {
// --- CHECKS ---
require(balances[msg.sender] >= _amount, "Insufficient balance");
// --- EFFECTS ---
balances[msg.sender] -= _amount; // State updated BEFORE external call
// --- INTERACTIONS ---
(bool success, ) = msg.sender.call{value: _amount}("");
require(success, "Failed to send Ether");
}
}Ograniczanie dostępu
Nie wszystkie funkcje smart kontraktu powinny być dostępne dla każdego. Kontrola dostępu gwarantuje, że określone wrażliwe operacje mogą wykonywać wyłącznie autoryzowane adresy lub role.
Typowe przykłady:
- Modyfikator
onlyOwnerdla funkcji administracyjnych. - Kontrola dostępu oparta na rolach (RBAC), w której różne role (np.
MINTER,PAUSER) mają określone uprawnienia.
Prawidłowa kontrola dostępu ma kluczowe znaczenie dla zapobiegania nieautoryzowanym działaniom i zachowania integralności kontraktu.
Funkcja ograniczona do właściciela
Oto sposób implementacji prostej kontroli dostępu onlyOwner za pomocą modyfikatora. Kontrakt przechowuje adres wdrażającego jako owner i tylko ten adres może wywołać funkcję setNewAdmin.
Ten wzorzec jest powszechnie stosowany w krytycznych funkcjach administracyjnych.
/// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract AccessControlled {
address public owner;
address public admin;
constructor() {
owner = msg.sender; // Deployer is the owner
admin = msg.sender;
}
modifier onlyOwner() {
require(msg.sender == owner, "Only owner can call this function");
_;
}
function setNewAdmin(address _newAdmin) public onlyOwner {
admin = _newAdmin;
}
function getAdmin() public view returns (address) {
return admin;
}
}Sprawdzenie najlepszych praktyk bezpieczeństwa
Dowiedziałeś(-aś) się o reentrancy, wzorcu CEI i kontroli dostępu. Które z poniższych stwierdzeń są prawdziwe w kontekście bezpiecznego tworzenia smart kontraktów?
Podsumowanie: bezpieczne kontrakty
Świetna praca! Poznałeś(-aś) podstawowe praktyki bezpieczeństwa smart kontraktów:
- Reentrancy: Krytyczna luka, w której zewnętrzne wywołania mogą ponownie „wejść” do funkcji przed aktualizacją stanu.
- Strażnicy przed reentrancy: Mechanizmy (takie jak mutexy lub modyfikatory) blokujące stan kontraktu podczas zewnętrznych wywołań.
- Wzorzec CEI: Zalecana kolejność operacji (Checks, Effects, Interactions), która gwarantuje aktualizację stanu przed zewnętrznymi wywołaniami.
- Kontrola dostępu: Ograniczanie dostępu do wrażliwych funkcji autoryzowanym adresom, często za pomocą modyfikatorów
onlyOwner.
Praktyki te mają kluczowe znaczenie dla tworzenia niezawodnych i godnych zaufania aplikacji zdecentralizowanych. Ćwicz dalej i zachowaj czujność!
Ucz się Web3 & DApp Development Fundamentals dzięki korepetycjom AI — za darmo
Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.
- Kursy
- 29
- Lekcje
- 105
Często zadawane pytania
Czy lekcja „Najlepsze praktyki bezpieczeństwa kontraktów” jest bezpłatna?
Tak — pełny tekst „Najlepsze praktyki bezpieczeństwa kontraktów” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Web3 & DApp Development Fundamentals, przejdź na CoddyKit PRO. Kurs Web3 & DApp Development Fundamentals zawiera 3 lekcji w sumie.
Co nauczysz się w „Najlepsze praktyki bezpieczeństwa kontraktów”?
Poznaj kluczowe praktyki bezpieczeństwa podczas tworzenia smart kontraktów, w tym zabezpieczenia przed reentrancy, wzorzec checks-effects-interactions i kontrolę dostępu. Ćwiczysz Web3 & DApp Development Fundamentals z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Web3 & DApp Development Fundamentals?
Nie wymagamy żadnego doświadczenia. Web3 & DApp Development Fundamentals w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 3.
Ile czasu zajmuje lekcja „Najlepsze praktyki bezpieczeństwa kontraktów”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Web3 & DApp Development Fundamentals?
Tak. Każda lekcja Web3 & DApp Development Fundamentals zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Standardy ERC (ERC-20, ERC-721)
- Najlepsze praktyki bezpieczeństwa kontraktów
- Kontrakty z możliwością aktualizacji