Ana içeriğe geç

Bu sayfanın güncellenmesine yardım edin

🌏

Bu sayfanın yeni bir sürümü vardır ancak şu anda yalnızca İngilizce'dir. Son sürümü çevirmemize yardımcı ol.

Sayfayı çevir
İngilizce'yi gör

Burada hata yok!🐛

Bu sayfa tercüme edilmiyor. Bu sayfayı şimdilik kasıtlı olarak İngilizce bıraktık.

Akıllı sözleşme güvenliği

Son düzenleme: , Invalid DateTime
Sayfayı düzenle

Ethereum akıllı sözleşmeleri aşırı esnektirler; hem büyük miktarlarda token tutabilirler (genelde 1 milyar ABD Dolarından fazla) hem de önceden dağıtılmış akıllı sözleşme kodunu esas alarak değişmez mantık çalıştırabilirler. Bu; güvene gerek olmayan ve birbirine bağlı akıllı sözleşmelerden oluşan renkli ve yaratıcı bir ekosistem oluşturmuş olsa da; akıllı sözleşmelerdeki güvenlik açıklarından ve Ethereum'daki beklenmedik davranışlardan faydalanarak kâr elde etmek isteyen saldırganları cezbetmek için mükemmel bir ekosistemdir. Akıllı sözleşme kodu genellikle güvenlik açıklarını onarmak için değiştirilemez; akıllı sözleşmelerden çalınan varlıklar kurtarılamaz ve çalınan varlıkların takibini yapmak aşırı derecede zordur. Akıllı sözleşme sorunlarına bağlı olarak çalınan veya kaybolan toplam değer miktarı, kuşkusuz 1 milyar ABD dolarının üzerindedir. Akıllı sözleşme kodlama hatalarına bağlı bazı büyük kayıplar:

Ön koşullar

Bu, akıllı sözleşme güvenliğini kapsadığı için güvenlik ile uğraşmadan önce akıllı sözleşmeler ile aşina olduğunuza emin olun.

Daha güvenli akıllı sözleşme kodu nasıl yazılır

Mainnet'e herhangi bir kod yüklemeden önce, akıllı sözleşmenize emanet edilen herhangi bir değerin korunması için yeterli önlem almak önemlidir. Bu makalede, birkaç özel saldırı hakkında konuşacağız, saldırı türleri hakkında bilgi edinmek için kaynaklar sağlayacağız ve sözleşmelerinizin doğru ve güvenli şekilde çalıştığından emin olmanız için size bazı temel araçlar ve en iyi yöntemleri sunacağız.

Denetimler gümüş bir kurşun değillerdir

Yıllar öncesinde akıllı sözleşmeleri yazma, derleme, test etme ve dağıtma araçları çok gelişmemişti, bu da birçok projenin gelişigüzel yollarla Solidity kodu yazmasına ve kodu, onun güvenli ve beklenen şekilde çalışıp çalışmadığını soruşturacak rastgele bir denetleyiciye atmasına yol açtı. 2020'de, Solidity yazımını destekleyen geliştirme süreçleri ve araçlar önemli ölçüde daha iyi; bu en iyi yöntemlerden faydalanmak sadece projenizin yönetiminin kolay olmasını sağlamakla kalmıyor, ayrıca projenizin güvenliği için hayati bir rol oynuyor. Akıllı sözleşmenizin yazımının sonundaki bir denetim, artık projenizin yapacağı tek güvenlik değerlendirmesi olarak yeterli değildir. Güvenlik, siz akıllı sözleşme kodunuzun ilk satırını yazmadan önce; doğru dizayn ve geliştirme süreçleri ile başlar.

Akıllı sözleşme geliştirme süreci

En az:

  • Tüm kod, git gibi bir sürüm denetimi sisteminde depolanır
  • Tüm kod değişiklikleri Çekme Talepleri aracılığıyla yapılır
  • Tüm Çekme Talepleri en az bir yorumcuya sahiptir. Eğer tek kişilik bir projeyseniz, başka bir tek yazar bulup kod değerlendirmeleri takas etmeyi göz önünde bulundurun!
  • Tek bir komut, bir Ethereum geliştirme ortamı (Bakınız: Truffle) kullanarak kodunuzu derler, dağıtır ve kodunuza karşı birtakım testler çalıştırır
  • Kodunuzu, ideal olarak her çekme talebi birleştirilmeden önce Mythril ve Slither gibi temel kod analizi araçlarından geçirerek, çıktıdaki farklılıkları karşılaştırdınız
  • Solidity HİÇBİR derleyici hatası vermez
  • Kodunuz iyi belgelenmiştir

Geliştirme süreci için daha denecek çok şey vardır, ama bu maddeler başlamak için iyi bir yerdir. Daha çok madde ve detaylı açıklamalar için, DeFiSafety tarafından sağlanan süreç kalite kontrol listesine bakınız. DefiSafety, çeşitli büyük ve halka açık Ethereum dApp'lerinin incelemelerini yayınlayan gayriresmî halka açık bir hizmettir. DeFiSafety derecelendirme sisteminin bir parçası, projenin süreç kalite kontrol listesine ne seviyede uyabildiğini kapsar. Bu süreçleri takip ederek:

  • Tekrarlanabilir, otomatikleştirilmiş testlerle daha güvenli kodlar ortaya çıkaracaksınız
  • Denetimciler projenizi daha verimli bir şekilde inceleyebilecek
  • Yeni geliştiriciler için daha kolay alışma süreci
  • Geliştiricilerin, değişiklikler üzerinde hızlıca tekrar yapmasını, test yapmasını ve geri bildirim almasını sağlayacaksınız
  • Projenizin gerilemeler yaşaması ihtimali düşer

Saldırılar ve güvenlik açıkları

Artık verimli bir geliştirme süreci kullanarak Solidity kodu yazdığınıza göre, nelerin yanlış gidebileceğini görmek için bazı yaygın Solidity zaafiyetlerine bakalım.

Yeniden giriş

Yeniden giriş, Akıllı Sözleşmeler geliştirirken göz önünde bulundurulması gereken en büyük ve en kayda değer güvenlik sorunlarından biridir. EVM'nin birden fazla sözleşmeyi aynı anda çalıştıramamasına rağmen, bir sözleşmenin farklı bir sözleşmeyi çağırması, çağrıyı yapan sözleşmenin yürütülmesini ve bellek durumunu çağrı dönene kadar durdurur, çağrı döndükten sonra da yürütme normal olarak devam eder. Bu durdurma ve yeniden başlatma, "yeniden giriş" olarak bilinen bir zaafiyeti oluşturabilir.

Burada yeniden girişe zaafı olan bir sözleşmenin basit bir versiyonu bulunuyor:

1// THIS CONTRACT HAS INTENTIONAL VULNERABILITY, DO NOT COPY
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
Tümünü göster
📋 Kopyala

Bir kullanıcının sözleşmeye daha önce depoladığı ETH'yi çekmesine izin vermek için, bu fonksiyon

  1. Bir kullanıcının ne kadar bakiyeye sahip olduğunu okur
  2. Ona o bakiye miktarını ETH olarak gönderir
  3. Onun bakiyesini, tekrar çekim yapamaması için sıfırlar.

Eğer sıradan bir hesaptan çağrılırsa (sizin kendi MetaMask hesabınız gibi), bu beklendiği gibi çalışır: msg.sender.call.value() sadece hesabınıza ETH gönderir. Ancak, akıllı sözleşmeler de çağrı yapabilir. Eğer özel, kötü amaçlı bir sözleşme withdraw() çağırıyorsa, msg.sender.call.value() sadece amount miktarında ETH göndermez, bunun yanında dolaylı olarak sözleşmeyi kod yürütmeye başlaması için çağırır. Bu kötü amaçlı sözleşmeyi hayal edin:

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
Tümünü göster
📋 Kopyala

Attacker.beginAttack() çağrısı yapmak, şöyle bir döngü başlatır:

10.) Attacker's EOA calls Attacker.beginAttack() with 1 ETH
20.) Attacker.beginAttack() deposits 1 ETH into Victim
3
4 1.) Attacker -> Victim.withdraw()
5 1.) Victim reads balances[msg.sender]
6 1.) Victim sends ETH to Attacker (which executes default function)
7 2.) Attacker -> Victim.withdraw()
8 2.) Victim reads balances[msg.sender]
9 2.) Victim sends ETH to Attacker (which executes default function)
10 3.) Attacker -> Victim.withdraw()
11 3.) Victim reads balances[msg.sender]
12 3.) Victim sends ETH to Attacker (which executes default function)
13 4.) Attacker no longer has enough gas, returns without calling again
14 3.) balances[msg.sender] = 0;
15 2.) balances[msg.sender] = 0; (it was already 0)
16 1.) balances[msg.sender] = 0; (it was already 0)
17
Tümünü göster

1 ETH ile Attacker.beginAttack çağrısı yapmak, saldırı Kurban'ına yeniden giriş saldırısı yapacaktır, bu da Kurban'ın sağladığından daha çok ETH çekecektir (diğer kullanıcıların bakiyelerinden alınır, Kurban sözleşmenin gerektiğinden az teminat göstermesine sebep olur)

Yeniden girişle nasıl başa çıkılır (yanlış yol)

Basitçe herhangi bir akıllı sözleşmenin kodunuzla etkileşime geçmesini engelleyerek yeniden giriş saldırısıyla başa çıkabileceğinizi düşünebilirsiniz. Stackoverflow'da arama yapıp, tonla beğeni almış olan bu kod parçacığını bulursunuz:

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

Görünüşte mantıklıdır: Sözleşmelerde kod bulunur, eğer çağıranda herhangi bir kod varsa yatırmasına izin verme. Hadi ekleyelim:

1// THIS CONTRACT HAS INTENTIONAL VULNERABILITY, DO NOT COPY
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)); // <- NEW LINE
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
Tümünü göster
📋 Kopyala

Artık ETH yatırmak için, adresinizde akıllı sözleşme kodu bulunmaması gerekir. Ancak bu, aşağıdaki Saldırgan sözleşmesiyle kolayca yenilebilir:

1contract ContractCheckAttacker {
2 constructor() public payable {
3 ContractCheckVictim(VICTIM_ADDRESS).deposit(1 ether); // <- New line
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
Tümünü göster
📋 Kopyala

İlk saldırı, sözleşme mantığına yönelik bir saldırı iken bu, Ethereum sözleşme dağıtım davranışına yönelik bir saldırıdır. İnşa esnasında, bir sözleşme henüz kodunu adresinde dağıtılması için döndürmemiştir, ancak bu süreç ESNASINDA tam EVM kontrolünü elinde tutar.

Teknik olarak akıllı sözleşmelerin kodunuzu çağırmasını engellemek bu satırı kullanarak mümkündür:

1require(tx.origin == msg.sender)
2
📋 Kopyala

Ancak, bu hâlâ iyi bir çözüm değildir. Ethereum'un en heyecan verici yönlerinden biri birleştirilebilirliğidir, akıllı sözleşmeler birbiriyle entegre olur ve birbirinin üzerinde inşa olurlar. Yukarıdaki satırı kullanarak, projenizin kullanılabilirliğini kısıtlıyorsunuz.

Yeniden girişle nasıl başa çıkılır (doğru yol)

Depolama güncelleme ve dış çağrının sırasını basitçe değiştirerek, saldırıya yol açan yeniden giriş durumunu engelliyoruz. Geri withdraw çağrısı yapmak, mümkün olsa da, balances depolaması hâlihazırda 0'a ayarlandığı için saldırgana katkı sağlamayacaktır.

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
📋 Kopyala

Yukarıdaki kod, yeniden girişe karşı koruyan "Kontroller-Tesirler-Etkileşimler" dizayn desenini takip eder. Burada Kontroller-Tesirler-Etkileşimler hakkında daha fazlasını okuyabilirsiniz

Yeniden girişle nasıl başa çıkılır (nükleer seçenek)

Güvenilmeyen bir adrese ETH gönderdiğiniz veya bilinmeyen bir sözleşme ile etkileşime geçtiğiniz (kullanıcı tarafından sağlanan bir token adresine transfer() çağrısı yapmak gibi) her zaman, kendinizi yeniden giriş ihtimaline açık hâle getiriyorsunuz. ETH göndermeyen veya güvenilmeyen sözleşmelere çağrı yapmayan sözleşmeler dizayn ederek, yeniden giriş ihtimalini engelliyorsunuz!

Daha fazla saldırı türü

Yukarıdaki saldırı türleri akıllı sözleşme kodlama problemlerini (yeniden giriş) ve Ethereum garipliklerini (kod sözleşme adresinde mevcut olmadan önce sözleşme constructor'ları içinde kod çalıştırmak) kapsar. Farkında olunması gereken daha birçok saldırı türü bulunmaktadır, örneğin:

  • Front-running
  • ETH gönderim reddi
  • Tam sayı taşması/yetersizliği

Daha fazla bilgi:

Güvenlik araçları

Ethereum güvenlik temellerini anlamanın ve profesyonel bir denetleme firmasına kodunuzu incelemeleri için ulaşmanın bir dengi olmasa da, kodunuzdaki potansiyel sorunları belirtmek için birçok araç bulunmaktadır.

Akıllı sözleşme güvenliği

Slither - Python 3'te yazılmış Solidity statik analiz altyapısıdır.

MythX - Ethereum akıllı sözleşmeleri için bir güvenlik analizi API'si.

Mythril - EVM bayt kodu için güvenlik analiz aracı.

Manticore - Akıllı sözleşmeler ve ikili dosyalar üzerinde sembolik bir yürütüm aracı kullanan bir komut satırı arabirimi.

Securify - Ethereum akıllı sözleşmeleri için güvenlik tarayıcısı.

ERC20 Verifier - Bir sözleşmenin ERC20 standardına uygun olup olmadığını kontrol etmek için kullanılan bir doğrulama aracı.

Resmi Doğrulama

Resmi Doğrulama Hakkında Bilgi

Araç kullanmak

Akıllı sözleşme güvenlik analizi için en popüler araçların ikisi şunlardır:

İkisi de kodunuzu analiz eden ve sorunları rapor eden kullanışlı araçlardır. Her biri [commercial] olarak sunulan sürümlere sahiptir ancak yerel olarak çalıştırmak için de ücretsiz sürümleri de bulunur. Aşağıda, kolay bir Docket imajı olan trailofbits/eth-security-toolbox ile kullanılabilir olan Slither'ın nasıl kullanılacağına dair kısa bir açıklama bulunuyor. Eğer henüz kurulu değilse Docker kurmanız gerekecektir.

$ 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

Şu çıktıyı oluşturacaktır:

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
Tümünü göster

Slither, bu konuda yeniden giriş için bir potansiyel tespit ederek sorunun gerçekleşebileceği önemli satırları tanımladı ve sorun hakkında daha fazla detay için bize bir bağlantı verdi:

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

Böylece kodunuzla ilgili potansiyel sıkıntıları hızlıca öğrenebilmenizi sağladı. Tüm otomatikleştirilmiş test araçları gibi, Slither mükemmel değildir ve aşırı rapor etmeye meyillidir. Suistimal edilebilir bir zaafiyet olmadığında bile sizi potansiyel bir yeniden giriş hakkında uyarabilir. Genelde kod değişiklikleri arasında Slither çıktısındaki FARKLARI incelemek aşırı derecede aydınlatıcıdır: Projenizin kodu tamamlanıncaya kadar beklemektense ortaya çıkan zaafiyetleri çok daha erken keşfetmeye yardımcı olur.

Daha fazla bilgi

Akıllı sözleşme güvenliği için en iyi yöntemlere dair kılavuzlar

Akıllı sözleşme güvenlik doğrulama standardı (SCSVS)

Size yardımcı olan bir topluluk kaynağı mı biliyorsunuz? Bu sayfayı düzenleyin ve onu ekleyin!

Bu makale yararlı oldu mu?