Segíts frissíteni ezt az oldalt

🌏

Az oldal új verziója jelenleg csak angolul érhető el. Segíts nekünk a fordításban.

Ez az oldal nincs befejezve. Ha a téma szakértője vagy, akkor légy szíves szerkeszd az oldalt és szórd meg a tudásoddal.

Biztonság

Utolsó módosítás: , Invalid DateTime
Edit page

Az Ethereum okosszerződések rendkívül flexibilisek, képesek nagy mennyiségű tokent tárolni (néha meghaladja az 1 Mrd. Usd-t) és megváltoztathatatlan logikát futtatni, mely korábban telepített okosszerződés kódon alapszik. Bár ez egy élénk és kreatív ökoszisztémát hozott létre a bizalom nélküli, egymással összekapcsolt okosszerződésekből, ugyanakkor tökéletes ökoszisztéma a profitra törekvő támadók számára is, aki az okosszerződések sebezhető pontjainak és az Ethereum váratlan viselkedésének kihasználásával szeretnének profitra szert tenni. Az okosszerződés kódot általában nem lehet megváltoztatni biztonsági hibák javítása céljából, az okosszerződésekből ellopott vagyont nem lehet visszaszerezni, és a lopott vagyont rendkívül nehéz nyomon követni. Az okosszerződés hibák miatt ellopott vagy elveszett érték teljes összege már könnyedén meghaladja az 1 Mrd. USD-t. A nagyobb okosszerződés hibák között van a:

Előfeltételek

Ez a cikk az okosszerződés biztonságról szól, így érdemes tisztában lenned az okosszerződésekkel, mielőtt belekezdenél a biztonságba.

Hogyan lehet biztonságosabb okosszerződés kódot írni

Mielőtt bármilyen kódot indítanánk a főhálózatra, fontos, hogy megfelelő elővigyázatossággal védjük meg az okosszerződésre rábízott értékeket. Ebben a cikkben néhány konkrét támadást megvitatunk, forrásokat biztosítunk további támadástípusok megismeréséhez, és hagyunk néhány alapvető eszközt és bevált gyakorlatot a szerződések megfelelő és biztonságos működéséhez.

Az audit nem gyógyír mindenre

Évekkel ezelőtt az okosszerződések írásának, fordításának, tesztelésének és telepítésének eszközei nagyon kiforratlanok voltak, ami sok projektet arra késztettet, hogy rendszertelenül írják a Solidity kódot, majd átadják egy auditornak, aki megvizsgálta a kódot annak biztosítása érdekében, hogy biztonságosan működik-e az elvárásoknak megfelelően. 2020-ban a Solidity írást támogató fejlesztési folyamatok és eszközök lényegesen jobbak; a bevált gyakorlatok felhasználása nemcsak a projekt könnyebb kezelhetőségét biztosítja, hanem a projekt biztonságának létfontosságú része is. Az okosszerződés megírásának végén végzett audit már nem elegendő, mint a projekted egyetlen biztonsági szempontja. A biztonság már az okosszerződés kód első sorának megírása előtt elkezdődik, a biztonság megfelelő tervezéssel és fejlesztési folyamatokkal kezdődik.

Okosszerződés fejlesztési folyamat

Minimum:

  • Az összes kódot egy verzió követő rendszer tárolja, mint a git
  • Minden kódmódosítást pull requesteken keresztül kell végezni
  • Minden pull requestet át kell néznie legalább egy valakinek. Ha egyedül vagy a projekten, akkor keress valakit aki szintén egyedül van és cseréljetek kód review-kat.
  • Egy egyedüli parancs fordítja, telepíti és futtatja a tesztek sorozatát a kódodra egy Ethereum fejlesztői környezet használatával (lásd Truffle)
  • Végig futtatod a kódodat valamilyen alapszintű kód analitikai eszközzel, mint a Mythril vagy a Slither, ideálisan mielőtt az egyes pull requesteket mergeled, így össze tudod hasonlítani a végeredményeket
  • A Solidity nem fog SEMMILYEN fordítói hibát visszaadni
  • A kódot megfelelően dokumentációval kell ellátni

Sokkal többet el lehetne mondani még a fejlesztési folyamatról, de ezek a tételek jó kiindulópontot jelentenek. További szempontért és részletes magyarázatért tekintsd meg a folyamat minőségi checklistát a DeFiSafety által. A DefiSafety egy nemhivatalos közszolgálat, mely értékeléseket publikál különböző nagyobb, nyilvános Ethereum dappról. A DeFiSafety minősítési rendszer egyik része, hogy a projekt mennyire tartja be ezt a folyamat minőségi ellenőrzőlistát. Ezeket a folyamatokat követve:

  • Biztonságosabb kódot állítasz elő úrjafelhasználható, automatizált tesztekkel
  • Az auditorok hatékonyabban fogják átnézni a projektedet
  • Könnyebben tudnak új fejlesztők becsatlakozni
  • A fejlesztők gyorsabban tudnak iterálni, tesztelni és visszajelzést kapni a módosításokról
  • Kisebb a valószínűsége, hogy projekted visszafejlődést szenved el

Támadások és sérülékenységek

Most, hogy a Solidity kódot már egy hatékony fejlesztési folyamat segítségével írod, nézzünk meg néhány általános Solidity biztonsági rést, hogy lássuk, mit ronthatunk el.

Újbóli belépés (re-entrancy)

Az újbóli belépés az egyik legnagyobb és legjelentősebb biztonsági probléma, melyet figyelembe kell venni okosszerződések fejlesztésekor. Míg az EVM nem tud egyszerre több szerződést futtatni, egy másik szerződést meghívó szerződés szünetelteti a hívó szerződés végrehajtását és memóriaállapotát, amíg a hívás vissza nem tér, ekkor a végrehajtás normálisan halad tovább. Ez a szüneteltetés és újraindítás egy "újbóli belépésnek" nevezett sérülékenységet eredményezhet.

Itt egy egyszerű szerződés verzió, mely ki van téve az újbóli belépésnek:

1// EZ A SZERZŐDÉS SZÁNDÉKOSAN SÉRÜLÉKENY, NE MÁSOLD LE
2contract Victim {
3 mapping (address => uint256) public balances;
4
5 function deposit() external payable {
6 balances[msg.sender] += msg.value;
7 }
8
9 function withdraw() external {
10 uint256 amount = balances[msg.sender];
11 (bool success, ) = msg.sender.call.value(amount)("");
12 require(success);
13 balances[msg.sender] = 0;
14 }
15}
16
Összes megjelenítése
📋 Másolás

Ahhoz, hogy egy felhasználó ETH-et utalhasson ki, amit korábban a szerződésben tárolt ez a függvény

  1. Leolvassa, a felhasználó egyenlegét
  2. Elküldi neki az egyenleg összegét ETH-ben
  3. Visszaállítja 0-ra az egyenleget, így nem tudja még egyszer kiutalni az összeget.

Ha meghívja egy normál számla (mint a saját Metamask számlád), akkor az elvártnak megfelelően működik: msg.sender.call.value() egyszerűen ETH-et küld a számládra. Azonban az okosszerződések is tudnak hívásokat intézni. Ha egy egyedi, rosszindulatú szerződés a withdraw() függvény meghívója, akkor a msg.sender.call.value() nem csak amount összegű ETH-et fog küldeni, hanem implicit módon meghívja a szerződést, hogy elindítsa a kód végrehajtást. Képzeld el ezt a következő rosszindulatú szerződést:

1contract 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}
13
Összes megjelenítése
📋 Másolás

Az Attacker.beginAttack() meghívása egy ciklust fog beindítani, mely valahogy így néz ki:

10.) A támadó EOA-ja (external owned account) meghívja a Attacker.beginAttack() függvényt 1 ETH-tel
20.) Az Attacker.beginAttack() beutal 1 ETH-et a Victim-be
3
4 1.) Attacker -> Victim.withdraw()
5 1.) Victim leolvassa a balanceOf[msg.sender] értéket
6 1.) Victim ETH-et küld Attacker-nek (mely végrehajtja az alapértelmezett függvényt)
7 2.) Attacker -> Victim.withdraw()
8 2.) Victim leolvassa a balanceOf[msg.sender] értéket
9 2.) Victim ETH-et küld Attacker-nek (mely végrehajtja az alapértelmezett függvényt)
10 3.) Attacker -> Victim.withdraw()
11 3.) Victim leolvassa a balanceOf[msg.sender] értéket
12 3.) Victim ETH-et küld Attacker-nek (mely végrehajtja az alapértelmezett függvényt)
13 4.) Attacker-nek nincs több gáza, hívás nélkül visszatér
14 3.) balances[msg.sender] = 0;
15 2.) balances[msg.sender] = 0; (már 0 volt az értéke)
16 1.) balances[msg.sender] = 0; (már 0 volt az értéke)
17
Összes megjelenítése

Az Attacker.beginAttack meghívása 1 ETH-tel egy újbóli belépés támadást fog indítani Victim ellen, ezzel több ETH-et kiutalva, mint amennyit beletesz (melyet más felhasználók egyenlegéből vont le, így a Victim szerződés alulfedezetté válik)

Hogyan kezeljük az újbóli belépést (a rosszabb mód)

Fontolóra lehet venni az újbóli belépés kezelését azzal, hogy egyszerűen megakadályozzuk az okosszerződések interakcióját a kóddal. A stackoverflow-n az alábbi kód részletet lehet megtalálni rengeteg pozitív szavazattal:

1function isContract(address addr) internal returns (bool) {
2 uint size;
3 assembly { size := extcodesize(addr) }
4 return size > 0;
5}
6
📋 Másolás

Értelmesnek tűnik: a szerződésnek van kódja, ha a hívónak van kódja, akkor nem engedi letétet elhelyezni. Adjuk hozzá ezt:

1// EZ A SZERZŐDÉS SZÁNDÉKOSAN SÉRÜLÉKENY, NE MÁSOLD LE
2contract ContractCheckVictim {
3 mapping (address => uint256) public balances;
4
5 function isContract(address addr) internal returns (bool) {
6 uint size;
7 assembly { size := extcodesize(addr) }
8 return size > 0;
9 }
10
11 function deposit() external payable {
12 require(!isContract(msg.sender)); // <- ÚJ SOR
13 balances[msg.sender] += msg.value;
14 }
15
16 function withdraw() external {
17 uint256 amount = balances[msg.sender];
18 (bool success, ) = msg.sender.call.value(amount)("");
19 require(success);
20 balances[msg.sender] = 0;
21 }
22}
23
Összes megjelenítése
📋 Másolás

Ebben az esetben ahhoz, hogy ETH letétet tudj elhelyezni, az címeden nem szabad okosszerződés kódnak lennie. Azonban ezt könnyen meg lehet kerülni a következő Attacker szerződéssel:

1contract ContractCheckAttacker {
2 constructor() public payable {
3 ContractCheckVictim(VICTIM_ADDRESS).deposit(1 ether); // <- Új sor
4 }
5
6 function beginAttack() external payable {
7 ContractCheckVictim(VICTIM_ADDRESS).withdraw();
8 }
9
10 function() external payable {
11 if (gasleft() > 40000) {
12 Victim(VICTIM_ADDRESS).withdraw();
13 }
14 }
15}
16
Összes megjelenítése
📋 Másolás

Míg az első támadás a szerződés logikája elleni támadás volt, ez az Ethereum szerződések telepítési viselkedése elleni támadás. A konstrukció alatt a szerződés nem adja vissza a kódját telepítettként a címén, de a teljes EVM kontrollt megtartja a folyamat ALATT.

Technikailag lehetséges megakadályozni az okosszerződéseket, hogy meghívják a kódodat ezzel a sorral:

1require(tx.origin == msg.sender)
2
📋 Másolás

Azonban ez még mindig nem egy jó megoldás. Az Ethereum egyik legizgalmasabb aspektusa az összeállíthatóság, amikor az okosszerződések integrálódnak és egymásra épülnek. A fenti sor használatával korlátozod a projekted hasznosságát.

Hogyan kezeljük az újbóli belépést (a jobb mód)

Egyszerűen a tárhely frissítés és a külső hívás sorrendjének felcserélésével meg tudjuk akadályozni az újbóli belépés feltételét, mely lehetővé tette a támadást. A withdraw visszahívása, amíg lehetséges, nem lesz jövedelmező a támadó számára, mivel a balances tárhely már 0 értékre lesz állítva.

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}
9
📋 Másolás

A fenti kód a "Checks-Effects-Interactions" tervezési mintát követi, amely segít megvédeni az újbóli belépéstől. Többet olvashatsz itt a Checks-Effects-Interactions-ról itt

Hogyan kezeljük az újbóli belépést (ágyúval verébre)

Bármikor amikor ETH-et küldesz egy nem megbízható címre vagy interakcióba lépsz egy ismeretlen szerződéssel (vagyis meghívod a transfer() függvényét egy felhasználó által biztosított token címnek), kitetté válsz egy lehetséges újbóli belépéses támadásnak. Olyan szerződések tervezésével, melyek sem az ETH küldést, sem a nem megbízható szerződések hívását sem támogatják, megelőzhető egy lehetséges újbóli belépés!

Több támadás típus

A fenti támadástípusok az okosszerződések kódjához (újbóli belépés) és az Ethereum furcsaságaihoz kapcsolódnak (kód futtatása a szerződés konstruktoron belül, mielőtt a kód elérhető lenne a szerződés címén). Sok, sok más fajta támadás típus létezik, melyekre figyelni kell, mint a:

  • Front-running
  • ETH küldés elutasítás
  • Integer overflow/underflow

További olvasnivaló:

Biztonsági eszközök

Bár nem helyettesítheti az Ethereum biztonsági alapismereteinek megértését és a szakmai auditáló cég bevonását a kód felülvizsgálatába, számos eszköz áll rendelkezésre a kódban felmerülő lehetséges problémák kiemelésére.

Okosszerződés Biztonság

Slither - Solidity statikus analízis keretrendszer Python 3-ban írva.

MythX - Biztonsági analízis API Ethereum okos szerződéseknek

Mythril - Biztonsági analitika eszköz EVM bájt-kódra.

SmartContract.Codes - Ellenőrzött Solidity forráskódok keresőmotora

Manticore - Egy Cli, ami egy szimbolikus futtató eszközt használ okosszerződésekre és binary-kre.

Securify - Biztonsági szkenner Ethereum okosszerződésekre.

ERC20 Verifier - Egy ellenőrző eszköz arra, hogy egy szerződés megfelel-e az ERC20 szabványnak.

Formális Ellenőrzés

Formális Ellenőrzés információ

Eszközök használata

A két legnépszerűbb okosszerződés biztonsági analitikai eszköz:

Mindkettő hasznos eszköz a kód elemzésére és a problémák jelentésére. Mindkettőnek van egy [commercial] hosztolt verziója, de ingyenesen is lehet őket lokálisan futtatni. Az alábbiakban bemutatunk egy gyors példát a Slither futtatására, amely egy kényelmes Docker image-dzsel érhető el: trailofbits/eth-security-toolbox. Szükség lesz a Docker telepítésére, ha még nincs feltelepítve.

$ mkdir test-slither
$ curl https://gist.githubusercontent.com/epheph/460e6ff4f02c4ac582794a41e1f103bf/raw/9e761af793d4414c39370f063a46a3f71686b579/gistfile1.txt > bad-contract.sol
$ docker run -v `pwd`:/share -it --rm trailofbits/eth-security-toolbox
docker$ cd /share
docker$ solc-select 0.5.11
docker$ slither bad-contract.sol

Ez a kimenetet fogja generálni:

ethsec@1435b241ca60:/share$ slither bad-contract.sol
INFO:Detectors:
Reentrancy in Victim.withdraw() (bad-contract.sol#11-16):
External calls:
- (success) = msg.sender.call.value(amount)() (bad-contract.sol#13)
State variables written after the call(s):
- balances[msg.sender] = 0 (bad-contract.sol#15)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities
INFO:Detectors:
Low level call in Victim.withdraw() (bad-contract.sol#11-16):
- (success) = msg.sender.call.value(amount)() (bad-contract.sol#13)
Reference: https://github.com/crytic/slither/wiki/Detector-Documentation#low-level-calls
INFO:Slither:bad-contract.sol analyzed (1 contracts with 46 detectors), 2 result(s) found
INFO:Slither:Use https://crytic.io/ to get access to additional detectors and Github integration
Összes megjelenítése

A Slither itt azonosította az újbóli belépés lehetőségét, meghatározta azokat a kulcsfontosságú sorokat, ahol a probléma felmerülhet, és linket adott a probléma további részleteihez:

Referencia: https://github.com/crytic/slither/wiki/Detector-Documentation#reentrancy-vulnerabilities

mely lehetővé teszi, hogy gyorsan megismerd a potenciális problémákat a kódoddal. Mint minden automatizált tesztelő eszköz, a Slither sem tökéletes, és a jelentések esetében hibázik túl sokat. Akkor is figyelmeztet potenciális újbóli belépésről, ha nincs is kihasználható sérülékenység. Gyakran a DIFFERENCE megtekintése a kód változtatások között a Slitherben rendkívüli felvilágosítással bírhat, mely segít felderíteni olyan sérülékenységeket, melyek sokkal korábban jöttek elő, minthogy a projekt kódja készen állt volna.

További olvasnivaló

Okosszerződés Biztonság Bevált Gyakorlatok Útmutató

Smart Contract Security Verification Standard (SCSVS)

Ismersz olyan közösségi anyagot, mely segített neked? Módosítsd az oldalt és add hozzá!

KövetkezőTárhely
👉