Web3:n ja DApp-sovellusten kehityksen perusteet · Oppitunti

Sopimusten tietoturvan parhaat käytännöt

Tutustu älysopimusten kehityksen keskeisiin tietoturvakäytäntöihin, kuten uudelleenkutsusuojauksiin, checks-effects-interactions-malliin ja käyttöoikeuksien hallintaan.

Oppitunti 2/311 vaihetta

Sopimusten tietoturvan parhaat käytännöt on ilmainen Web3:n ja DApp-sovellusten kehityksen perusteet-oppitunti CoddyKitissä. Tämä on oppitunti 2/3. Voit lukea tästä oppimispolusta kokonaan mitkä tahansa 3 oppituntia ilmaiseksi — sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä käytännön harjoittelun sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Oppitunti kuuluu Web3:n ja DApp-sovellusten kehityksen perusteet-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Web3:n ja DApp-sovellusten kehityksen perusteet-kurssilla on yhteensä 3 oppituntia.

Älysopimusten tietoturvan perusteet

Tervetuloa älysopimusten tietoturvan kriittiseen maailmaan! Toisin kuin perinteisessä ohjelmistossa, älysopimusten virheet voivat johtaa varojen peruuttamattomaan menettämiseen.

Koska älysopimuksia ei voi muuttaa käyttöönoton jälkeen, haavoittuvuuksien korjaaminen on erittäin vaikeaa. Se edellyttää usein monimutkaisia päivitysmekanismeja tai jopa uuden sopimuksen käyttöönottoa.

Tässä oppitunnissa tutustumme keskeisiin käytäntöihin, joiden avulla rakennetaan turvallisempia ja vankempia älysopimuksia.

Uudelleenkutsun ymmärtäminen

Yksi tunnetuimmista haavoittuvuuksista on uudelleenkutsu (reentrancy). Se syntyy, kun ulkoinen kutsu toiseen sopimukseen tai osoitteeseen "palaa" kutsuvaan sopimukseen ennen kuin alkuperäisen funktion tilapäivitykset on tehty.

Kuvittele pankkiautomaatti, josta voi nostaa rahaa. Jos se veloittaa tililtäsi vasta *sen jälkeen*, kun se on antanut rahat, uudelleenkutsuhyökkäys vastaisi sitä, että pyytäisit rahaa toistuvasti ennen kuin järjestelmä päivittää saldosi ja tyhjentäisit automaatin.

Haavoittuva nostokoodi

Tarkastellaan tätä yksinkertaistettua sopimusta, jossa käyttäjä voi tallettaa ja nostaa Etheriä. Huomaatko vaaran?

withdraw()-funktio lähettää ensin Etherin ja päivittää sitten saldon. Hyökkääjä voi kutsua withdraw()-funktiota uudelleen haitallisesta sopimuksestaan ulkoisen kutsun aikana, ennen kuin hänen saldokseen asetetaan nolla.

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

Uudelleenkutsujen estäminen: tilalukot

Yleinen ja tehokas tapa estää uudelleenkutsut on käyttää uudelleenkutsusuojaa (reentrancy guard). Siinä sopimuksen tila lukitaan ulkoisen kutsun ajaksi ja avataan sen jälkeen.

Jos uudelleenkutsuva kutsu yrittää suorittaa lukitun funktion, se kumoutuu. OpenZeppelinin ReentrancyGuard on suosittu toteutus, mutta voit rakentaa myös yksinkertaisen suojan itse.

Uudelleenkutsusuojan toteuttaminen

Lisätään yksinkertainen uudelleenkutsusuoja totuusarvomuuttujan avulla. Näin varmistetaan, ettei withdraw-funktiota voi kutsua uudelleen ennen nykyisen suorituksen päättymistä ja tilan päivittämistä.

nonReentrant-muokkain asettaa lukituksen, suorittaa toiminnon ja vapauttaa sitten lukituksen.

/// 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-malli

Checks-Effects-Interactions (CEI) -malli on keskeinen tietoturvan parhaiden käytäntöjen mukainen toimintatapa. Se määrittää funktiossa suoritettavien toimien järjestyksen:

  • Checks: Tarkista ehdot (esimerkiksi require-lauseet ja onlyOwner).
  • Effects: Päivitä sopimuksen tila (esimerkiksi balances[msg.sender] -= amount).
  • Interactions: Tee ulkoisia kutsuja muihin sopimuksiin tai osoitteisiin.

CEI-mallin noudattaminen auttaa estämään erilaisia hyökkäyksiä, kuten uudelleenkutsuja, koska se varmistaa sopimuksen tilan viimeistelyn *ennen* ulkoisia kutsuja.

CEI nostofunktiossa

Huomaa, kuinka SecureBank-sopimuksemme withdraw-funktio noudattaa jo CEI-mallia:

  • Checks: Muokkaimen require(balances[msg.sender] >= _amount, ...) ja require(!_locked, ...).
  • Effects: balances[msg.sender] -= _amount; päivittää tilan.
  • Interactions: msg.sender.call{value: _amount}(""); suorittaa ulkoisen siirron.

Tämä järjestys on ratkaisevan tärkeä uudelleenkutsujen estämiseksi ja tilan johdonmukaisuuden varmistamiseksi.

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

Käyttöoikeuksien rajoittaminen

Kaikkien ei pitäisi voida kutsua kaikkia älysopimuksen funktioita. Käyttöoikeuksien hallinta varmistaa, että vain valtuutetut osoitteet tai roolit voivat suorittaa tiettyjä arkaluonteisia toimintoja.

Yleisiä esimerkkejä ovat:

  • onlyOwner-muokkain hallinnollisia toimintoja varten.
  • Roolipohjainen käyttöoikeuksien hallinta (RBAC), jossa eri rooleilla (esimerkiksi MINTER ja PAUSER) on tietyt käyttöoikeudet.

Asianmukainen käyttöoikeuksien hallinta on välttämätöntä luvattomien toimien estämiseksi ja sopimuksen eheyden säilyttämiseksi.

Omistajalle rajoitettu funktio

Näin toteutetaan yksinkertainen onlyOwner-käyttöoikeuksien hallinta muokkaimen avulla. Sopimus tallentaa käyttöönottajan osoitteen owner-muuttujaan, ja vain tämä osoite voi kutsua setNewAdmin-funktiota.

Tätä mallia käytetään laajasti kriittisissä hallinnollisissa toiminnoissa.

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

Tietoturvan parhaiden käytäntöjen testi

Olette oppineet uudelleenkutsuista, CEI-mallista ja käyttöoikeuksien hallinnasta. Mitkä seuraavista väitteistä pitävät paikkansa turvallisessa älysopimuskehityksessä?

Kertaus: turvalliset sopimukset

Hienoa työtä! Olette oppineet älysopimusten tietoturvan keskeiset käytännöt:

  • Uudelleenkutsu: Kriittinen haavoittuvuus, jossa ulkoiset kutsut voivat "palata" funktioon ennen tilapäivityksiä.
  • Uudelleenkutsusuojat: Mekanismit, kuten mutex-lukot tai muokkaimet, joilla sopimuksen tila lukitaan ulkoisten kutsujen ajaksi.
  • CEI-malli: Suositeltu toimintojen järjestys (Checks, Effects, Interactions), jolla varmistetaan tilan päivittäminen ennen ulkoisia kutsuja.
  • Käyttöoikeuksien hallinta: Arkaluonteisten funktioiden rajoittaminen valtuutetuille osoitteille, usein onlyOwner-muokkaimilla.

Nämä käytännöt ovat ratkaisevan tärkeitä vankkojen ja luotettavien hajautettujen sovellusten rakentamisessa. Jatkakaa harjoittelua ja pysykää valppaina!

Aloita maksutta

Opi Web3:n ja DApp-sovellusten kehityksen perusteet tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
29
Oppitunnit
105

Usein kysytyt kysymykset

Onko oppitunti ”Sopimusten tietoturvan parhaat käytännöt” ilmainen?

Kyllä — voit lukea täällä verkossa kokonaan ilmaiseksi mitkä tahansa Web3:n ja DApp-sovellusten kehityksen perusteet-oppimispolun 3 oppituntia, myös oppitunnin “Sopimusten tietoturvan parhaat käytännöt”. Sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä interaktiiviset harjoitukset sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Web3:n ja DApp-sovellusten kehityksen perusteet-kurssilla on yhteensä 3 oppituntia.

Mitä opin oppitunnilla ”Sopimusten tietoturvan parhaat käytännöt”?

Tutustu älysopimusten kehityksen keskeisiin tietoturvakäytäntöihin, kuten uudelleenkutsusuojauksiin, checks-effects-interactions-malliin ja käyttöoikeuksien hallintaan. Harjoittelet Web3:n ja DApp-sovellusten kehityksen perusteet-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni Web3:n ja DApp-sovellusten kehityksen perusteet-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin Web3:n ja DApp-sovellusten kehityksen perusteet-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 2/3.

Kuinka kauan ”Sopimusten tietoturvan parhaat käytännöt”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä Web3:n ja DApp-sovellusten kehityksen perusteet-oppitunnilla?

Kyllä. Jokainen Web3:n ja DApp-sovellusten kehityksen perusteet-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. ERC-standardit (ERC-20, ERC-721)
  2. Sopimusten tietoturvan parhaat käytännöt
  3. Päivitettävät sopimukset
← Takaisin: Web3:n ja DApp-sovellusten kehityksen perusteet