1. Anasayfa
  2. Solidity

Solidity Yeniden Giriş Saldırısı

Solidity Akıllı Sözleşmesinde Yeniden Giriş Saldırısı Nedir, Nasıl Önlenir?

Solidity Yeniden Giriş Saldırısı
Solidity Yeniden Giriş Saldırısı
0

Solidity Yeniden Giriş Saldırısı

Reentrancy Attack (Yeniden Giriş Saldırısı), Ethereum ve diğer blockchain platformlarındaki en yaygın saldırılardan biridir. Bu yazıda yeniden giriş saldırısının nasıl çalıştığına ve nasıl önleneceğine bakacağız.

Yeniden Giriş Saldırısı Nedir?

Yeniden giriş saldırısı, birden çok özyineleme düzeyine izin veren bir program üzerinde gerçekleştirilen kötü amaçlı bir istismardır. Genel olarak, saldırgan kötü amaçlı bir yineleme döngüsü oluşturarak bir programın durumunu alt üst eder. Yeniden giriş saldırıları kavramı, Ethereum veya akıllı sözleşmelerle sınırlı değildir. Yeniden giriş saldırıları, JavaScript ve web uygulama dilleri dahil olmak üzere özyinelemeyi destekleyen herhangi bir bilgi işlem platformunda mümkündür.

Reentrancy Saldırısı Nasıl Tespit Edilir?

Ethereum akıllı sözleşmelerinde yeniden giriş ciddi bir saldırı vektörüdür. Aynı işlem içerisinde birden fazla fonksiyonun çalıştırılabilmesinden kaynaklanan bir güvenlik zafiyetidir. Bu, bir saldırganın bir akıllı sözleşmenin birden çok işlevini çağırmasına ve bu işlevler tamamlandıktan sonra bile durumunda değişiklik yapmasına olanak tanır. Güvenlik açıkları söz konusu olduğunda çoğu şeyde olduğu gibi, bunları tespit etmenin en iyi yolu, akıllı sözleşme denetim hizmetinin bir parçası olarak yaptığımız manuel, ayrıntılı kaynak kodu incelemesidir . Aşağıdaki örnekte, yeniden giriş saldırısına karşı savunmasız bir kodumuz var.

pragma solidity 0.8.7;

contract InsecureEtherVault {
    mapping (address => uint256) private userBalances;

    function deposit() external payable {
        userBalances[msg.sender] += msg.value;
    }

    function withdrawAll() external {
        uint256 balance = getUserBalance(msg.sender);
        require(balance > 0, "Insufficient balance");

        (bool success, ) = msg.sender.call{value: balance}("");
        require(success, "Failed to send Ether");

        userBalances[msg.sender] = 0;
    }

    function getBalance() external view returns (uint256) {
        return address(this).balance;
    }

    function getUserBalance(address _user) public view returns (uint256) {
        return userBalances[_user];
    }
}  

Bu Solidity sözleşmesi, bir Ether hazinesi olarak tasarlanmış gibi görünüyor. Kullanıcılar sözleşmeye Ether yatırabilir ve daha sonra sözleşmeden tüm bakiyelerini çekebilir.

Sözleşmenin işleyişi şöyle çalışır:

  1. deposit fonksiyonu, kullanıcıların sözleşmeye Ether yatırmasına izin verir. Bu fonksiyon çağrıldığında, kullanıcının gönderdiği Ether miktarı userBalances haritasına eklenir.
  2. withdrawAll fonksiyonu, kullanıcının tüm bakiyesini çekmesine izin verir. Bu fonksiyon çağrıldığında, kullanıcının bakiyesi getUserBalance fonksiyonu ile öğrenilir ve daha sonra kullanıcıya gönderilir. Bakiyenin sıfırlandıktan sonra, kullanıcının bakiyesi userBalances haritasında güncellenir.
  3. getBalance fonksiyonu, sözleşmenin bakiyesini döndürür. Bu fonksiyonun ne işe yaradığı hakkında daha fazla bilgiye ihtiyacım var, çünkü sözleşmenin bakiyesi hiçbir zaman kullanılmıyor ve sözleşmenin amacının ne olduğu belirsiz.
  4. getUserBalance fonksiyonu, belirtilen kullanıcının bakiyesini döndürür. Bu fonksiyon withdrawAll fonksiyonu tarafından kullanılır ve kullanıcının bakiyesi hakkında bilgi verir.

Bu solidity akıllı sözleşmesindeki güvenlik açıkları konusunda ChatGPT’ye danıştığımda verdiği cevap şöyle oldu.

Solidity Akıllı Sözleşmesinde Yeniden Giriş Saldırısı Tespiti via ChatGPT
Solidity Akıllı Sözleşmesinde Yeniden Giriş Saldırısı Tespiti via ChatGPT

Durumu şöyle ifade edebiliriz.

withdrawAll” işlevi, kullanıcıya akıllı sözleşmede kaydedilen ve tanımlanan bakiyesini öder. Ödemeyi gerçekleştirmek için kullanılan yaklaşım şu şekildedir: “(bool success, ) = msg.sender.call{value: balance}("");

Solidity dilinde msg.sender, akıllı sözleşmenin çağrıldığı adrestir. Bu adres bir Ethereum cüzdanı veya saldırganın akıllı sözleşme adresi olabilir, eğer güvenlik açığı bulunan koddaki acceptAll işlevi ondan çağrılırsa.

Sorun, güvenlik açığı bulunan kodun, içeriği kötü amaçlı olabilecek çağrı işlevini körü körüne çağırmasıdır. Saldırgan bu zafiyetten faydalanabilmesi için zafiyet kodunun çağrılacağı sözleşmenin call fonksiyonunun içeriğine aynı fonksiyona tekrar bir call ekleyecektir.

Yeniden Giriş Saldırısı Nasıl Önlenir?

Bu tür saldırılara karşı koruma aslında oldukça basittir ve bu günlerde klasik (web) uygulamalarda bulduğumuz yarış durumu güvenlik açıklarını biraz hatırlatır. Üst düzey fikir, işlevin zaten yürütülüp yürütülmediğini görmek için işlevi yürütmenin başına bir kontrol eklemek, ardından locked = true özelliğini eklemek ve belirteçleri göndermektir. Bundan sonra, fonksiyonun tekrar serbest olması için locked özelliğini false‘a döndürelim.

function withdraw() external {
​ ​ ​​require(!locked);
​ ​ ​​locked = true;​ ​ 
​​  uint userBalance = userBalances[msg.sender];
 ​ ​​require(userBalance > 0);
​ ​ ​​(bool success,) = msg.sender.call{ ​​value: ​userBalance ​​}("");
​ ​ ​​require(success,);
​ ​ ​​userBalances[msg.sender] = 0;​ ​ ​​
  locked = false;
}

Bu kod parçası, withdraw fonksiyonunu tanımlar ve kullanıcıların sözleşmeden bakiyelerini çekmelerine izin verir. Bu fonksiyonun işleyişi şöyle çalışır:

  1. locked değişkeninin değeri false olup olmadığını kontrol eder. Eğer locked değişkeni true ise, fonksiyon çağrısı iptal edilir ve bir hata mesajı gönderilir. Bu mekanizma, fonksiyonun birden fazla kez çağrılmasını engellemek için kullanılır.
  2. userBalances haritasından kullanıcının bakiyesi alınır ve 0’dan büyük olup olmadığı kontrol edilir. Eğer bakiyesi 0 veya daha az ise, fonksiyon çağrısı iptal edilir ve bir hata mesajı gönderilir.
  3. msg.sender (yani fonksiyonu çağıran kullanıcı) adresine userBalance miktarındaki Ether gönderilir. Eğer gönderme başarısız olursa, fonksiyon çağrısı iptal edilir ve bir hata mesajı gönderilir.
  4. Kullanıcının bakiyesi userBalances haritasında sıfırlanır.
  5. locked değişkeni false olarak ayarlanır ve fonksiyon tamamlanır.

Bu fonksiyon, kullanıcıların sözleşmeden bakiyelerini çekmesine izin verir ancak withdrawAll fonksiyonu ile benzer bir işlevi yerine getirir.

Zero To Hero : Web3.0 ve Solidity Geliştirme Yol Haritası 2023

Solidity Programlama Dili Öğrenme yolculuğunuz hakkında daha iyi rehberlik almak için Solidity nedir? Ethereum Akıllı Sözleşmelerinin Dili Rehberi içeriğimize göz atın. Dilerseniz Yeni Başlayanlar için Solidity – Akıllı Sözleşme Geliştirme Hızlandırılmış Kursuna katılın.

Çalışmaya nereden başlayacağım diyenler için Blockchain ​​Developer Olmak İçin Yol Haritası içeriğine de muhakkak bakın.

Gelin aklınızdaki soruları SUPERPEER sohbetinde cevaplayalım.

Bu makaleyi okuduğunuz için teşekkürler! Bana destek olmak isterseniz;

Beni TwitterLinkedin ve YouTube‘da takip edin.

Kısa bir yorum bırakmayı UNUTMAYIN!

Hasan YILDIZ, Girişimci. Doktora Öğrencisi. Yazmayan YAZILIMCI. Veri Şeysi. Eğitmen...

Yazarın Profili

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir