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:
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.withdrawAll
fonksiyonu, kullanıcının tüm bakiyesini çekmesine izin verir. Bu fonksiyon çağrıldığında, kullanıcının bakiyesigetUserBalance
fonksiyonu ile öğrenilir ve daha sonra kullanıcıya gönderilir. Bakiyenin sıfırlandıktan sonra, kullanıcının bakiyesiuserBalances
haritasında güncellenir.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.getUserBalance
fonksiyonu, belirtilen kullanıcının bakiyesini döndürür. Bu fonksiyonwithdrawAll
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.
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:
locked
değişkeninin değerifalse
olup olmadığını kontrol eder. Eğerlocked
değişkenitrue
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.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.msg.sender
(yani fonksiyonu çağıran kullanıcı) adresineuserBalance
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.- Kullanıcının bakiyesi
userBalances
haritasında sıfırlanır. locked
değişkenifalse
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.
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 Twitter, Linkedin ve YouTube‘da takip edin.
Kısa bir yorum bırakmayı UNUTMAYIN!