Serangan Reentrancy dalam Smart Contract: Memahami Kerentanan dan Menerapkan Strategi Pencegahan

Serangan reentrancy merupakan salah satu kerentanan paling terkenal dalam keamanan kontrak pintar, yang bertanggung jawab atas jutaan dana yang dicuri di berbagai protokol blockchain. Artikel ini menjelaskan mekanisme di balik kerentanan reentrancy dan menyajikan teknik pencegahan yang komprehensif yang harus diterapkan oleh pengembang yang peduli terhadap keamanan.

Apa itu Serangan Reentrancy?

Pada dasarnya, serangan reentrancy terjadi ketika sebuah fungsi dalam smart contract dipanggil berulang kali sebelum eksekusi sebelumnya selesai. Kerentanan mendasar muncul ketika sebuah smart contract memanggil kontrak eksternal sebelum menyelesaikan perubahan statusnya sendiri, menciptakan peluang untuk dieksploitasi.

Dalam skenario tipikal, Kontrak A berinteraksi dengan Kontrak B dengan memanggil salah satu fungsinya. Kerentanan keamanan yang kritis muncul ketika Kontrak B mendapatkan kemampuan untuk memanggil kembali ke dalam Kontrak A sementara Kontrak A masih menjalankan fungsi aslinya. Pola interaksi rekursif ini menciptakan dasar untuk potensi eksploitasi.

Bagaimana Serangan Reentrancy Bekerja: Penjelasan Langkah-demi-Langkah

Pertimbangkan skenario ini: Kontrak A memegang total 10 ETH, dengan Kontrak B telah menyetorkan 1 ETH ke dalam Kontrak A. Kerentanan menjadi dapat dieksploitasi ketika Kontrak B mencoba untuk menarik dananya melalui urutan berikut:

  1. Kontrak B memanggil fungsi withdraw() di Kontrak A
  2. Kontrak A memverifikasi bahwa saldo Kontrak B lebih besar dari 0 (lulus pemeriksaan)
  3. Kontrak A mengirim ETH ke Kontrak B, memicu fungsi fallback Kontrak B
  4. Sebelum Kontrak A dapat memperbarui saldo Kontrak B menjadi nol, fungsi fallback di Kontrak B memanggil withdraw() lagi
  5. Kontrak A memeriksa saldo Kontrak B, yang masih menunjukkan 1 ETH (belum diperbarui)
  6. Proses diulang hingga dana Kontrak A habis

Kerentanan utama adalah bahwa pembaruan saldo terjadi setelah transfer ETH, memungkinkan penyerang untuk mengeksploitasi saldo yang sama beberapa kali sebelum diatur ke nol.

Anatomi Serangan: Implementasi Teknis

Mari kita periksa pola kode yang rentan:

solidity // Kontrak EtherStore yang rentan kontrak EtherStore { mapping(address => uint) publik saldo;

fungsi deposit() publik dapat dibayar { 
    balances[msg.sender] += msg.value;
}

fungsi tarikSemua() publik {
    uint bal = balances[msg.sender];
    require(bal > 0);
    
    // Kerentanan: Panggilan eksternal sebelum pembaruan status
    (bool dikirim, ) = msg.sender.call{value: bal}("");
    require(kirim, "Gagal mengirim Ether");
    
    // Pembaruan status terjadi terlalu terlambat
    balances[msg.sender] = 0;
}

}

Sekarang, mari kita lihat bagaimana seorang penyerang mungkin mengeksploitasi kerentanan ini:

solidity // Kontrak serangan yang memanfaatkan kerentanan reentrancy kontrak Attack { EtherStore publik etherStore;

konstruktor(alamat _etherStoreAddress) {
    etherStore = EtherStore(_alamatEtherStore);
}

// Fungsi fallback yang dipanggil ketika EtherStore mengirim Ether
terima() eksternal dapat dibayar {
    jika(alamat(etherStore).saldo >= 1 ether) {
        // Masukkan kembali fungsi withdrawAll
        etherStore.withdrawAll();
    }
}

fungsi attack() eksternal dapat dibayar {
    require(msg.value >= 1 ether);
    
    // Setor untuk menetapkan saldo di EtherStore
    etherStore.deposit{value: 1 ether}();
    
    // Mulai proses penarikan, memicu serangan reentrancy
    etherStore.withdrawAll();
}

}

Urutan serangan dimulai ketika penyerang memanggil attack(). Ini menyetor 1 ETH untuk menetapkan saldo dan kemudian memanggil withdrawAll(). Ketika EtherStore mengirimkan ETH kembali, itu memicu fungsi receive() dalam kontrak Attack, yang memanggil withdrawAll() lagi sebelum saldo diperbarui. Loop ini berlanjut sampai EtherStore kehabisan dana.

Teknik Pencegahan Komprehensif

Pengembang yang sadar akan keamanan dapat menerapkan tiga teknik yang kuat untuk melindungi dari kerentanan reentrancy:

1. Perlindungan Tingkat Fungsi: Modifier nonReentrant

Perlindungan yang paling umum di tingkat fungsi individu adalah menerapkan pengaman reentransi:

solidity kontrak ReentrancyGuard { bool pribadi terkunci = false;

modifier nonReentrant() {
    require(!terkunci, "Panggilan berulang");
    terkunci = true;
    _;
    terkunci = false;
}

// Terapkan modifier ini ke fungsi yang rentan
fungsi tarikDana() publik nonReentrant {
    // Kode fungsi dilindungi dari reentrancy
}

}

Pendekatan ini secara efektif mengunci kontrak selama eksekusi fungsi, mencegah panggilan rekursif sampai fungsi selesai dan membuka kembali status.

2. Perlindungan Lintas Fungsi: Pola Pemeriksaan-Dampak-Interaksi

Pola keamanan fundamental ini merestrukturisasi kode untuk menghilangkan kerentanan di berbagai fungsi:

solidity // IMPLEMENTASI YANG RENTAN fungsi tarikSemua() publik { uint bal = saldos[msg.sender]; require(bal > 0);

(bool sent, ) = msg.sender.call{value: bal}("");
require(sent, "Gagal mengirim Ether");

balances[msg.sender] = 0; // Pembaruan status setelah interaksi

}

// IMPLEMENTASI AMAN fungsi withdrawAll() publik { uint bal = balances[msg.sender]; require(bal > 0); // Memeriksa

balances[msg.sender] = 0; // Efek (perubahan status)

(bool sent, ) = msg.sender.call{value: bal}(""); // Interaksi
require(sent, "Gagal mengirim Ether");

}

Dengan mengikuti pola Checks-Effects-Interactions, kontrak memperbarui statusnya sebelum interaksi eksternal, memastikan bahwa bahkan jika penyerang mencoba untuk masuk kembali, mereka akan menemui status terkini (saldo nol).

3. Perlindungan Tingkat Proyek: Penjaga Reentrancy Global

Untuk proyek kompleks dengan banyak kontrak yang saling berinteraksi, menerapkan pengaman reentrancy global memberikan perlindungan di seluruh sistem:

solidity kontrak GlobalReentrancyGuard { bool private _notEntered = true;

modifier globalNonReentrant() {
    require(_notEntered, "ReentrancyGuard: panggilan reentrant");
    _notEntered = false;
    _;
    _notEntered = true;
}

}

// Semua kontrak dalam proyek mewarisi dari GlobalReentrancyGuard kontrak SecureContract adalah GlobalReentrancyGuard { fungsi vulnerableOperation() publik globalNonReentrant { // Dilindungi dari reentrancy di seluruh proyek } }

Pendekatan ini sangat berharga untuk melindungi terhadap serangan reentrancy lintas kontrak dalam protokol DeFi di mana banyak kontrak saling berinteraksi.

Dampak Dunia Nyata dari Kerentanan Reentrancy

Kerentanan reentrancy telah menyebabkan beberapa eksploitasi paling merusak dalam sejarah blockchain. Peretasan DAO yang terkenal pada tahun 2016 mengakibatkan pencurian sekitar 3,6 juta ETH ( yang bernilai sekitar $50 juta pada saat itu ) dan akhirnya menyebabkan hard fork Ethereum yang menciptakan Ethereum Classic.

Lebih baru-baru ini, pada tahun 2020, protokol Lendf.Me kehilangan sekitar $25 juta akibat serangan reentrancy, yang menyoroti bahwa meskipun kesadaran meningkat, kerentanan ini terus menimbulkan risiko signifikan bagi keamanan kontrak pintar.

Praktik Terbaik Keamanan

Selain teknik spesifik yang disebutkan, pengembang harus mengikuti praktik keamanan tambahan ini:

  1. Selalu gunakan versi Solidity terbaru yang menawarkan fitur keamanan yang lebih baik
  2. Subjek kontrak untuk audit keamanan menyeluruh oleh perusahaan terkemuka
  3. Terapkan cakupan pengujian yang komprehensif termasuk pengujian unit untuk kasus-kasus tepi
  4. Mulailah dengan eksposur ETH minimal saat menerapkan kontrak baru ke produksi
  5. Pertimbangkan verifikasi formal untuk kontrak bernilai tinggi

Dengan menerapkan teknik defensif ini dan mengikuti praktik keamanan terbaik, pengembang dapat secara signifikan mengurangi risiko serangan reentrancy dan membangun kontrak pintar yang lebih aman untuk aplikasi blockchain.

ETH-0.39%
ETC-1.52%
Lihat Asli
Halaman ini mungkin berisi konten pihak ketiga, yang disediakan untuk tujuan informasi saja (bukan pernyataan/jaminan) dan tidak boleh dianggap sebagai dukungan terhadap pandangannya oleh Gate, atau sebagai nasihat keuangan atau profesional. Lihat Penafian untuk detailnya.
  • Hadiah
  • Komentar
  • Posting ulang
  • Bagikan
Komentar
0/400
Tidak ada komentar
  • Sematkan
Perdagangkan Kripto Di Mana Saja Kapan Saja
qrCode
Pindai untuk mengunduh aplikasi Gate
Komunitas
Bahasa Indonesia
  • 简体中文
  • English
  • Tiếng Việt
  • 繁體中文
  • Español
  • Русский
  • Français (Afrique)
  • Português (Portugal)
  • Bahasa Indonesia
  • 日本語
  • بالعربية
  • Українська
  • Português (Brasil)