Grundlæggende udvikling af Web3- og DApp-programmer · Lektion

Bedste praksis for contract-sikkerhed

Opdag vigtige sikkerhedspraksisser til udvikling af smart contracts, herunder reentrancy guards, mønstret checks-effects-interactions og adgangskontrol.

Lektion 2 af 311 trin

Bedste praksis for contract-sikkerhed er en gratis Grundlæggende udvikling af Web3- og DApp-programmer-lektion på CoddyKit. Dette er lektion 2 af 3. Du kan læse alle 3 lektioner i dette læringsspor gratis i deres fulde længde — derefter låser CoddyKit PRO alle lektioner op samt praktiske øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Den er en del af læringsforløbet i Grundlæggende udvikling af Web3- og DApp-programmer, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Grundlæggende udvikling af Web3- og DApp-programmer-kurset indeholder 3 lektioner i alt.

Introduktion til sikkerhed i smarte kontrakter

Velkommen til den kritiske verden inden for sikkerhed i smarte kontrakter! I modsætning til traditionel software kan fejl i smarte kontrakter føre til uigenkaldelige tab af midler.

Da smarte kontrakter ikke kan ændres efter implementering, er det ekstremt vanskeligt at rette sårbarheder. Det kræver ofte komplekse opgraderingsmekanismer eller endda implementering af en ny kontrakt.

I denne lektion gennemgår vi vigtige metoder til at bygge mere sikre og robuste smarte kontrakter.

Forstå reentrancy

En af de mest berygtede sårbarheder er reentrancy. Den opstår, når et eksternt kald til en anden kontrakt eller adresse "går ind i" den kaldende kontrakt igen, før den oprindelige funktions tilstandsopdateringer er færdige.

Forestil dig en hæveautomat, der lader dig hæve penge. Hvis den først trækker beløbet fra din konto *efter* at have givet dig kontanterne, svarer et reentrancy-angreb til gentagne gange at bede om kontanter, før systemet opdaterer din saldo, så hæveautomaten tømmes.

Sårbar kode til hævning

Se på denne forenklede kontrakt, hvor en bruger kan indsætte og hæve Ether. Kan du få øje på faren?

Funktionen withdraw() sender først Ether og opdaterer derefter saldoen. En angriber kan kalde withdraw() igen fra sin ondsindede kontrakt under det eksterne kald, før saldoen sættes til nul.

/// 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;
    }
}

Forebyggelse af reentrancy: Låsning af tilstanden

En almindelig og effektiv måde at forhindre reentrancy på er at bruge en reentrancy-vagt. Det indebærer, at kontraktens tilstand låses under et eksternt kald og låses op igen bagefter.

Hvis et reentrant-kald forsøger at udføre den låste funktion, rulles kaldet tilbage. OpenZeppelin's ReentrancyGuard er en populær implementering, men du kan også bygge en enkel version selv.

Implementering af en reentrancy-vagt

Lad os tilføje en enkel reentrancy-vagt ved hjælp af et boolesk flag. Det sikrer, at funktionen withdraw ikke kan kaldes igen, før den aktuelle udførelse er færdig, og tilstanden er blevet opdateret.

Modifikatoren nonReentrant sætter en lås, udfører handlingen og frigiver derefter låsen.

/// 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;
    }
}

CEI-mønsteret

Checks-Effects-Interactions-mønsteret (CEI) er en grundlæggende bedste praksis inden for sikkerhed. Det fastlægger en bestemt rækkefølge for handlinger i en funktion:

  • Kontroller: Kontrollér betingelser (f.eks. require-udsagn og onlyOwner).
  • Effekter: Opdatér kontraktens tilstand (f.eks. balances[msg.sender] -= amount).
  • Interaktioner: Udfør eksterne kald til andre kontrakter eller adresser.

Ved at følge CEI kan du forhindre forskellige angreb, herunder reentrancy, fordi kontraktens tilstand er afsluttet *før* eksterne kald.

CEI i hævefunktionen

Bemærk, hvordan SecureBanks withdraw-funktion allerede følger CEI-mønsteret:

  • Kontroller: require(balances[msg.sender] >= _amount, ...) og require(!_locked, ...) fra modifikatoren.
  • Effekter: balances[msg.sender] -= _amount; opdaterer tilstanden.
  • Interaktioner: msg.sender.call{value: _amount}(""); udfører den eksterne overførsel.

Denne rækkefølge er afgørende for at forhindre reentrancy og sikre en konsistent tilstand.

/// 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");
    }
}

Begrænsning af adgang

Ikke alle funktioner i en smart kontrakt bør kunne kaldes af alle. Adgangskontrol sikrer, at kun godkendte adresser eller roller kan udføre bestemte følsomme handlinger.

Almindelige eksempler omfatter:

  • En onlyOwner-modifikator til administrative funktioner.
  • Rollebaseret adgangskontrol (RBAC), hvor forskellige roller (f.eks. MINTER og PAUSER) har bestemte tilladelser.

Korrekt adgangskontrol er afgørende for at forhindre uautoriserede handlinger og bevare kontraktens integritet.

Ejerbegrænset funktion

Her kan du se, hvordan du implementerer en enkel adgangskontrol med onlyOwner ved hjælp af en modifikator. Kontrakten gemmer implementørens adresse som owner, og kun denne adresse kan kalde funktionen setNewAdmin.

Dette mønster bruges i vid udstrækning til kritiske administrative funktioner.

/// 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;
    }
}

Kontrol af bedste praksis inden for sikkerhed

Du har lært om reentrancy, CEI-mønsteret og adgangskontrol. Hvilke af følgende udsagn er sande med hensyn til sikker udvikling af smarte kontrakter?

Opsummering: Sikre kontrakter

Godt arbejde! Du har forstået grundlæggende sikkerhedspraksisser for smarte kontrakter:

  • Reentrancy: En kritisk sårbarhed, hvor eksterne kald kan "gå ind i" en funktion igen, før tilstanden opdateres.
  • Reentrancy-vagter: Mekanismer (som mutexer eller modifikatorer), der låser kontraktens tilstand under eksterne kald.
  • CEI-mønsteret: Den anbefalede rækkefølge for handlinger (Checks, Effects, Interactions), som sikrer, at tilstanden opdateres før eksterne kald.
  • Adgangskontrol: Begrænsning af følsomme funktioner til godkendte adresser, ofte ved hjælp af onlyOwner-modifikatorer.

Disse metoder er afgørende for at bygge robuste og troværdige decentraliserede applikationer. Bliv ved med at øve dig, og vær opmærksom!

Gratis at komme i gang

Lær Grundlæggende udvikling af Web3- og DApp-programmer med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
29
Lektioner
105

Ofte stillede spørgsmål

Er lektionen “Bedste praksis for contract-sikkerhed” gratis?

Ja — alle 3 lektioner i læringssporet Grundlæggende udvikling af Web3- og DApp-programmer, inklusive “Bedste praksis for contract-sikkerhed”, kan læses gratis i deres fulde længde her på webstedet. Derefter låser CoddyKit PRO alle lektioner op samt interaktive øvelser med en indbygget kodeeditor og en AI-underviser døgnet rundt. Grundlæggende udvikling af Web3- og DApp-programmer-kurset indeholder 3 lektioner i alt.

Hvad lærer jeg i “Bedste praksis for contract-sikkerhed”?

Opdag vigtige sikkerhedspraksisser til udvikling af smart contracts, herunder reentrancy guards, mønstret checks-effects-interactions og adgangskontrol. Du øver dig i Grundlæggende udvikling af Web3- og DApp-programmer med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Grundlæggende udvikling af Web3- og DApp-programmer?

Der kræves ingen tidligere erfaring. Grundlæggende udvikling af Web3- og DApp-programmer på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 2 af 3.

Hvor lang tid tager lektionen “Bedste praksis for contract-sikkerhed”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Grundlæggende udvikling af Web3- og DApp-programmer-lektion?

Ja. Alle Grundlæggende udvikling af Web3- og DApp-programmer-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. ERC-standarder (ERC-20, ERC-721)
  2. Bedste praksis for contract-sikkerhed
  3. Opgraderbare contracts
← Tilbage til Grundlæggende udvikling af Web3- og DApp-programmer