Solidity'de Input Validation Saldırılarını Nasıl Önlerim

Makale yazarı

Eksik giriş doğrulama saldırıları, Solidity sözleşmelerinin kullanıcı kontrollü adresleri, tutarları, kimlikleri, fiyatları veya konfigürasyon değerlerini, sözleşmenin ekonomik ve güvenlik varsayımlarını karşılayıp karşılamadığını kanıtlamadan kabul ettiği durumlarda ortaya çıkar. Bu tür saldırıları önlemek için, varlıklar hareket etmeden veya muhasebe değişiklikleri yapılmadan önce güven sınırıyla her dış girdiyi allowlistler, sınır kontrolleri, sıfır adres kontrolleri, yetkilendirme ve durum invariantsıyla doğrula.

Etki somut: Qubit, 2022-01-28 tarihinde BSC üzerinde Eksik Giriş Doğrulama olarak sınıflandırılan olayda 80.0 milyon dolar kaybetti. Bu başarısızlıklar sadece hatalı işlem sorunları değil. Kontrol edilmemiş bir değer, geçerli Solidity kodunun geçersiz bir iş modeli eylemi gerçekleştirmesine neden olabilir; örneğin, var olmayan teminatı krediye eklemek, varlıkları güvenilmeyen bir hedefe transfer etmek veya borç sınırını aşmak gibi.

Solidity’de giriş doğrulama açığı nedir ve bir saldırı nasıl işler?

Solidity’de giriş doğrulama açığı, bir sözleşmenin, çağıran tarafından sağlanan değeri güvenilir kabul ettiği durumdur; o değer, izinleri, varlık muhasebesini, fiyatlandırmayı veya durum geçişlerini kontrol ettiğinde. Bir saldırgan, sözleşmenin ABI açısından sözde geçerli, ancak protokol açısından ekonomik açıdan geçersiz olan bir değer sağlar ve ardından ortaya çıkan durumu kullanarak varlık çekebilir, talepler üretebilir veya kısıtlamaları aşabilir.

Solidity, türleri uygular, protokol niyetini değil. Bir uint256 herhangi bir hatasız sayı olabilir, bir address ise bir EOA, kötü niyetli bir sözleşme, sıfır adres veya desteklenmeyen bir token olabiliyor. Sözleşme, eksik semantik kuralları zorunlu kılmalı.

Tipik saldırı dizisi şu şekildedir:

  1. Saldırgan, kontrol edilmemiş bir tutar, token, alıcı, fiyat veya kimlik kabul eden genel bir fonksiyonu tanımlar.
  2. Saldırgan, protokolün niyet ettiği varsayımı ihlal eden bir değer seçer.
  3. Sözleşme, güvenli olmayan bir bakiye, hak, borç limiti veya yetkilendirme durumu kaydeder.
  4. Saldırgan, zehirlenmiş duruma güvenen daha sonra bir fonksiyon çağırır.
  5. Protokol, bu yanlış duruma dayanarak varlık transferi yapar veya devredilebilir bir talep üretir.
  6. Diğer kullanıcılar, muhasebe açığının görünür hale gelmesiyle kayba uğrar.

Eleştirel soru şudur: bu girişin temsil ettiği gerçek nedir ve sözleşme bu gerçeği bağımsız olarak nerede doğrular? Eğer cevap “çağıran sağladıysa” ise, sözleşmenin yanlış bir giriş doğrulama açığı vardır.

Hangi Solidity desenleri giriş doğrulama saldırılarına neden olur?

Giriş doğrulama saldırıları genellikle, bir sözleşmenin entegrasyonlar veya kullanıcı deneyimi için esneklik kabul ettiği durumlarda, bu esnekliği protokolün desteklediği alanla sınırlandırmadığı zaman ortaya çıkar. En tehlikeli parametre genellikle açıkça price veya amount olarak adlandırılmaz; bu bir token adresi, rota, alıcı, tranche kimliği, kanıt veya dış sözleşme adresi olabilir.

Yaygın güvensiz desenler şunlardır:

  • Çağırandan bildirilen teminat değeri: Bir borç alan, kredi gücünü hesaplamak için kullanılan değeri sağlar.
  • Rastgele token adresleri: Bir depo fonksiyonu, transfer davranışları standart olmayan herhangi bir ERC-20 kabul eder.
  • Kontrol edilmemiş alıcı adresleri: Bir fonksiyon address(0), sözleşmenin kendisi veya emanet kurallarını ihlal eden adresleri kabul eder.
  • Sınırsız yüzdeler ve oranlar: Bir ücret, LTV, iskonto, slippage veya ödül oranı, öngörülen temel puan aralığını aşar.
  • Doğrulanmamış dizi ilişkileri: Eş zamanlı diziler uzunluk açısından farklıdır, tekrar eden girişler kabul edilir veya beklenmedik şekilde bir varlık tekrar eden rota.
  • Önkoşullar olmadan durum geçişleri: Bir talep, iptal, geri ödeme veya göç eylemi yanlış yaşam döngüsü durumunda çalışır.
  • Gerçekmiş gibi kabul edilen dış veri: Bir fonksiyon, çağıran tarafından sağlanan fiyatlara, bakiyelere, imzalara veya dönüş değerlerine doğrulama olmadan güvenir.

Doğrulama bağlam içinde olmalı. require(amount > 0) faydalı olsa da, bunun amount’ın karşılanabilir, teminatlı, sınır içinde veya şu anki durumla tutarlı olduğunu kanıtlamaz. Aynı şekilde, bir token adresinin sıfır olmayan olup olmadığını kontrol etmek, tokenin onaylandığını, beklendiği gibi davrandığını veya doğru piyasaya ait olduğunu göstermez.

Soken’ın inceleme metodolojisi, her dış parametreyi bir hareket durumu olarak görür: etkileyebileceği invariants’ı tespit edin ve sonra fonksiyon, bu invariant’ı yazarak değiştirmeden önce doğrular.

Solidity’de giriş doğrulama örneği nasıl görünür?

Bu giriş doğrulama saldırısı örneğinde, borç alan kendi collateralValue değerini sunabilir. Sözleşme, istenen kredinin bu değerden büyük olmadığını kontrol eder, fakat borç alanın bu tutarda teminat yatırdığını hiçbir zaman doğrulamaz. Bu nedenle, çağıran hem değeri hem de ihtiyari değeri rastgele ayarlayabilir ve borçlandırıcıdan fon elde edebilir.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IERC20 {
    function transfer(address to, uint256 value) external returns (bool);
}

contract UnsafeLender {
    IERC20 public immutable asset;
    mapping(address => uint256) public debt;

    constructor(IERC20 asset_) {
        require(address(asset_) != address(0), "sıfır varlık");
        asset = asset_;
    }

    function borrow(
        uint256 collateralValue,
        uint256 amount
    ) external {
        require(collateralValue > 0, "teminat yok");
        require(amount <= collateralValue, "yetersiz teminat");

        // HASARLI: borç alan, krediyi destekleyen değeri sağlar.
        debt[msg.sender] += amount;

        require(asset.transfer(msg.sender, amount), "aktarma başarısız");
    }
}

Düzeltilmiş tasarım, kredi kapasitesini gerçekten alınan teminattan türetir. Immutable varlık adresleri, dar bir allowlist görevi görür; yapıcı fonksiyon sıfır adresleri reddeder, LTV sınırlandırılmıştır ve bakiye farkı kontrolü, talep edilen depozito tutarına güvenmek yerine transferInvariant’ını doğrular.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

interface IERC20 {
    function balanceOf(address account) external view returns (uint256);
    function transfer(address to, uint256 value) external returns (bool);
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

contract ValidatedLender {
    IERC20 public immutable loanAsset;
    IERC20 public immutable collateralAsset;
    uint256 public immutable maxLtvBps;

    mapping(address => uint256) public collateral;
    mapping(address => uint256) public debt;

    constructor(IERC20 loanAsset_, IERC20 collateralAsset_, uint256 maxLtvBps_) {
        require(address(loanAsset_) != address(0), "sıfır kredi varlığı");
        require(address(collateralAsset_) != address(0), "sıfır teminat varlığı");
        require(maxLtvBps_ > 0 && maxLtvBps_ <= 10_000, "geçersiz LTV");
        loanAsset = loanAsset_;
        collateralAsset = collateralAsset_;
        maxLtvBps = maxLtvBps_;
    }

    function depositCollateral(uint256 amount) external {
        require(amount > 0, "sıfır tutar");
        uint256 beforeBalance = collateralAsset.balanceOf(address(this));
        require(collateralAsset.transferFrom(msg.sender, address(this), amount), "aktarım başarısız");
        uint256 received = collateralAsset.balanceOf(address(this)) - beforeBalance;

        // DÜZELTME: sadece gerçekten alınan teminatı dikkate al.
        require(received == amount, "desteklenmeyen token davranışı");
        collateral[msg.sender] += received;
    }

    function borrow(uint256 amount) external {
        require(amount > 0, "sıfır tutar");
        uint256 limit = (collateral[msg.sender] * maxLtvBps) / 10_000;
        require(debt[msg.sender] + amount <= limit, "kredi sınırı aşıldı");

        debt[msg.sender] += amount;
        require(loanAsset.transfer(msg.sender, amount), "aktarma başarısız");
    }
}

Eğer protokolünüz esnek varlıklar, rotalar veya muhasebe girdileri kabul ediyorsa, saldırgan akıllı sözleşme incelemesi ve penetrasyon testi hizmeti her bir iddia edilen değerin fonlara etki etmeden önce bağımsız olarak belirlendiğini izleyebilir.

Solidity’de giriş doğrulama saldırılarını nasıl önlersiniz?

Giriş doğrulama saldırılarını önlemek, girdileri girişte doğrulamayı ve sözleşme yaşam döngüsü boyunca bu varsayımları açık invariants ile korumayı gerektirir. En iyi savunma tek bir modifier değil; anlamlı her durum değişikliğinin açıkça tanımlanmış izin verilen girişler ve sonra doğrulanabilir bir durumu kalan tasarımdır.

Teknik Ne durdurur Gas veya karmaşıklık maliyeti Sınırlama
Token ve uygulama allowlistleri Desteklenmeyen ERC-20’ler, kötü niyetli adaptörler, rastgele harici çağrılar Depolama arama veya immutable adres karşılaştırması Onaylanan tokenler ekonomik riski yine de getirebilir
Sınır kontrolleri Sıfır tutarlar, aşırı oranlar, geçersiz temel puanlar, güvensiz son teslim süreleri Düşük, genellikle birkaç karşılaştırma Sınırlar tek başına ekonomik doğruluk sağlamaz
Sıfır adres ve alıcı kontrolleri Varlık yakma, kullanılamaz sahiplik, geçersiz emanet hedefleri Düşük, bir adres karşılaştırması Sıfır olmayan adres yine güvenilmez olabilir
Yetkilendirme ve yaşam döngüsü kontrolleri Yanlış durumda veya yanlış aktör tarafından çağrı Depolama okuma ve rol tasarımı Roller güvenli yönetim gerektirir
Bakiye farkı doğrulaması Yanlış depo kredi ve desteklenmeyen token transfer davranışı Ek token bakiyesi okuma Rebase varlıkların dikkatli yönetimini gerektirir
Invariant doğrulaması Bozuk ödeme kabiliyeti, arz, borç ve rezerv ilişkileri Ek okuma ve aritmetik Invariants, tüm ekonomik modeli kapsamalıdır

Yapılandırılabilir protokoller için, yapılandırmayı hem oluştururken hem de kullandığında doğrula. Bir yönetişim tarafından belirlenen parametre, oluşturulduğunda güvenli olabilir; ancak bir bağımlılık değişince güvensiz hale gelir. Yeniden kullanılabilir sistemlerde, başlatıcı parametreleri, genel fonksiyon girişleri kadar titizlikle doğrula.

Güvenlik içgörüsü: Soken metodolojisi, invariant’ten başlar, require ifadesinden değil. İncelerler, bir kullanıcı kontrollü giriş işlendiğinde neyin doğru kalması gerektiğini söyleyemiyorsa, doğrulamanın yeterli olup olmadığını belirleyemezler.

Eksik giriş doğrulama olarak sınıflandırılan gerçek olaylar nelerdir?

Eksik giriş doğrulama, borç verme, takas ve varlık yönetimi kontekstinde maddi akıllı sözleşme kayıplarıyla ilişkilendirilmiştir. Bu olay kategorisi, farklı zincirler ve protokol tasarımlarıyla genişler; yanlış giriş doğrulamasının bir protokol- mantık hatası olduğu ve ağ spesifik Solidity hatası olmadığı gösterir.

Protokol Tarih Kaybedilen tutar Zincir
Qubit 2022-01-28 80.0 Milyon ABD Doları BSC
Hedgey 2024-04-19 44.7 Milyon ABD Doları Arbitrum, Ethereum
Transit Swap 2022-10-02 28.9 Milyon ABD Doları Ethereum, BSC
Pickle 2020-11-22 19.7 Milyon ABD Doları Ethereum

Bu olayların ortak dersi: sözleşmeler, sadece bir işlem bu değeri kodlayıp geçtikten sonra protokol gerçekliğini güvenli şekilde çıkaramaz. Varlık sistemleri, teminat alınan değer, desteklenen varlık, yetkilendirilmiş rota veya geçerli yaşam döngüsü geçişi gibi, değeri koruyan belirli özellikler around semantik doğrulama gerektirir.

DeFiLlama, 2020’den 2026’ya kadar 52 akıllı sözleşme olayını Eksik Giriş Doğrulama altında sınıflandırır ve toplamda 330.5 milyon dolar kayıp bildirmiştir. Bu genişlik, Soken’ın doğrulama incelemesini yüzeysel require ifadeleri listesi değil, ekonomik ve güvenlik odaklı bir değerlendirme olarak görmesine neden olur.

Eksik giriş doğrulama açıklarını nasıl tespit edebilirsiniz?

Eksik giriş doğrulama açıklarını, her dış erişilebilir parametrenin, etkileşime geçtiği depolama yazma, dış çağrı ve varlık transferleriyle izleyerek tespit edebilirsiniz. Sonra, geçersiz ama ABI açısından geçerli değerlerin, invariant’ı ihlal edip etmediğini sınar. Statik analiz bazı eksik kontrolleri bulabilir, ancak protokol spesifik varsayımların doğrulanması için senaryo tabanlı testler gereklidir.

Bu inceleme kontrol listesini kullanın:

  1. Değeri, otoriteyi veya yönlendirmeyi etkileyen her external ve public fonksiyon parametresini listeleyin.
  2. Her adresin sıfır olmayan, onaylanmış ve rolüne uygun olup olmadığını belirleyin.
  3. Her tutarı protokol sınırları, bakiye, borç limiti, arz sınırları ve ondalık varsayımlarıyla kontrol edin.
  4. Fiyatların, teminat değerlerinin ve bakiyelerin güvenilir ya da bağımsız olarak doğrulanmış kaynaklardan geldiğinden emin olun.
  5. Dizilerin uzunluklarını, benzersizliğini, sıralamasını ve çapraz dizi indeks ilişkilerini doğrulayın.
  6. Her fonksiyonun sadece geçerli yaşam döngüsü durumlarına ve yetkili çağıranlara izin verdiğinden emin olun.
  7. Ücret, rebase ve özel ERC-20 davranışları gibi bakiye farkı muhasebesini test edin.
  8. Her varlık hareketinin sonrası durumu ve durumu sınayın.

Slither, tehlikeli harici çağrılar, kontrol edilmemiş dönüş değerleri ve zayıf erişim kontrolleri gibi desenleri tespit edebilir; ancak her protokol invariant’ini çıkaramaz, bu nedenle Foundry fuzzing ile sıfır, maksimum değerler, çift diziler, beklenmedik adresler ve desteklenmeyen tokenlar oluşturun.

Invariant testleri özellikle etkilidir. Örneğin, toplam dış borcun, yapılandırılmış kredi kapasitesini aşmadığını, kredilendirilen teminatın alınan varlıkları geçtiğini ve yalnızca onaylanan varlıkların bir piyasa muhasebesini değiştirebildiğini test edin. Bu özellikler etrafında geniş bir güvenlik inceleme süreci kurarken Soken Hub’ı keşfedin.

Giriş doğrulama, kullanıcı niyeti ile protokol gerçekliği arasındaki sınırdır. Bir sözleşme, sadece çağıranın iddiasına dayanarak kredi, yetkilendirme, fiyatlandırma veya transfer yapmamalıdır. Duyarlı değerleri doğrulanmış durumlardan türetin, kabul edilen alanları sınırlandırın ve sonucu ekonomik olarak güvenli hale getiren invariant’ı zorunlu kılın.

Pratik adım, kod tabanınızdaki her varlık hareket fonksiyonu için parametre-invariant haritası oluşturmaktır. Belgelenmiş güven kaynağı, izin verilen aralık ve sonrası durum olmadan herhangi bir parametre hemen inceleme gerektirir.

Makale yazarı

Sıkça Sorulan Sorular

Solidity'de input validation açığı nedir?

Solidity'de input validation açığı, sözleşmenin kullanıcı kontrollü adresleri, tutarları, kimlikleri, fiyatları veya yapılandırma değerlerini, güvenlik ve ekonomik varsayımlara uygun olup olmadığını kanıtlamadan kabul ettiği durumdur. Bu, geçersiz işlem veya güvensiz varlık transferleri gibi hatalara yol açabilir.

Solidity'de input validation saldırılarını nasıl önlerim?

Her dış girdiyi asset hareketi veya muhasebe değişikliği öncesinde doğrulamak gerekir. Allowlist, sınır kontrolleri, zero-address kontrolleri, yetkilendirme ve durum invariants ile birlikte kullanarak, kullanıcı kontrollü değerlerin güvenlik ve ekonomik kurallara uygun olmasını sağlayın.

Qubit input validation saldırısında ne oldu?

Qubit, 2022-01-28'de BSC üzerinde 80 milyon dolar kaybetti ve bu olay Missing Input Validation olarak sınıflandırıldı. Bu olay, unchecked girişlerin sadece malformed transaction sorunlarına yol açmadığını, aynı zamanda geçersiz ekonomik işlemler veya varlık kabulüne neden olabildiğini gösteriyor.

Solidity sözleşmelerinin zero address kontrolü yapması neden önemli?

Kullanıcı tarafından sağlanan adreslerin gerçekten varış, varlık veya yetkiyi belirtmesi gerekiyorsa, zero address kabul edilmemeli. Bu, transfer, yetkilendirme veya muhasebe varsayımlarını ihlal eden placeholder değerlerin kabul edilmesini engeller.

Sınır kontrolleri, input validation açıklarını önlemek için yeterli mi?

Sadece sınır kontrolleri yeterli değildir çünkü değer, sayısal aralıkta olabilir ama yine de yetkilendirme, allowlist, adres veya durum gereksinimlerini ihlal edebilir. Sözleşmeler, assets hareketi veya muhasebe değişikliği öncesinde layered validation yapmalı, her girdiyi ilgili iş kuralı ile eşleştirmelidir.

Sohbet