Keamanan Smart Contract: Memahami dan Mencegah Kerentanan Reentrancy

Serangan reentrancy merupakan salah satu ancaman keamanan yang paling signifikan dalam pengembangan kontrak pintar. Analisis teknis ini menjelaskan mekanisme di balik kerentanan reentrancy dan menyediakan strategi pertahanan yang komprehensif untuk melindungi kontrak Anda.

Apa itu Serangan Reentrancy?

Reentrancy terjadi ketika sebuah fungsi dalam kontrak pintar dapat terganggu selama eksekusinya dan dipanggil kembali sebelum pemanggilan pertama selesai. Dalam istilah teknis, reentrancy mengeksploitasi konteks eksekusi dari kontrak pintar dengan memanipulasi alur kontrol melalui panggilan eksternal.

Ketika Kontrak A berinteraksi dengan Kontrak B, kerentanan muncul karena Kontrak B dapat memanggil kembali ke Kontrak A sementara eksekusi Kontrak A masih berlangsung. Pola pemanggilan rekursif ini dapat dimanfaatkan untuk memanipulasi status kontrak dan menguras dana.

Mekanika Serangan: Bagaimana Reentrancy Bekerja

Pertimbangkan skenario dengan dua kontrak:

  • Kontrak A: Kontrak yang rentan yang menyimpan 10 ETH
  • Kontrak B: Sebuah kontrak jahat dengan 1 ETH disetorkan di Kontrak A

Alur serangan mengikuti pola ini:

  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 1 ETH ke Kontrak B sebelum memperbarui catatan saldonya
  4. Transfer ETH memicu fungsi fallback dari Kontrak B
  5. Di dalam fungsi fallback, Kontrak B memanggil penarikan Kontrak A lagi ()
  6. Karena Kontrak A belum memperbarui saldo Kontrak B, pemeriksaan berhasil lagi.
  7. Kontrak A mengirimkan ETH lain ke Kontrak B
  8. Siklus ini diulang sampai Kontrak A kehabisan dana

Kerentanan kritis terletak pada urutan eksekusi Kontrak A: ia melakukan panggilan eksternal ( mengirim ETH) sebelum memperbarui keadaan internalnya ( mengatur saldo menjadi nol).

Tiga Teknik Pertahanan Terhadap Reentrancy

1. Perlindungan Tingkat Fungsi dengan Modifier noReentrant

Modifier noReentrant mengimplementasikan mekanisme penguncian yang mencegah fungsi dipanggil secara rekursif:

solidity // Variabel status untuk melacak reentrancy bool private locked = false;

// Modifikasi untuk mencegah reentrancy modifier noReentrant() { require(!locked, "Reentrant call"); terkunci = true; _; terkunci = false; }

// Fungsi terlindungi fungsi tarik() publik noReentrant { // Logika fungsi di sini }

Pendekatan ini memblokir upaya reentrancy dengan mempertahankan variabel status yang berlaku untuk seluruh kontrak yang mencegah eksekusi bersamaan dari fungsi-fungsi yang dilindungi.

2. Pola Cek-Efek-Interaksi

Pola ini merestrukturisasi kode untuk mengikuti urutan operasi tertentu:

  1. Periksa: Verifikasi kondisi ( misalnya, saldo > 0)
  2. Efek: Memperbarui variabel status ( misalnya, set saldo = 0)
  3. Interaksi: Lakukan panggilan eksternal (misalnya, transfer ETH)

Membandingkan kode yang rentan vs. kode yang dilindungi:

Rentan: solidity fungsi withdraw() eksternal { uint bal = balances[msg.sender]; require(bal > 0);

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

balances[msg.sender] = 0; // Mungkin tidak pernah tercapai jika terjadi reentrancy

}

Dilindungi: soliditas fungsi withdraw() eksternal { uint bal = balances[msg.sender]; require(bal > 0);

// Efek sebelum Interaksi (aman)
balances[msg.sender] = 0;

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

}

Dengan memperbarui status sebelum interaksi eksternal, kontrak tetap aman meskipun panggilan eksternal memicu panggilan fungsi reentrant.

3. Perlindungan Lintas Kontrak dengan GlobalReentrancyGuard

Untuk proyek dengan beberapa kontrak yang saling berinteraksi, pengaman reentrancy bersama memberikan perlindungan yang komprehensif:

solidity // Kontrak pusat untuk perlindungan reentrancy kontrak GlobalReentrancyGuard { mapping(alamat => bool) pribadi _status;

function _beforeNonReentrant() internal {
    require(_status[msg.sender] == false, "ReentrancyGuard: reentrant call");
    _status[msg.sender] = true;
}

function _afterNonReentrant() internal {
    _status[msg.sender] = false;
}

}

// Menggunakan penjaga dalam sebuah kontrak kontrak ProtectedContract adalah GlobalReentrancyGuard { fungsi protectedFunction() eksternal { _beforeNonReentrant();

    // Logika fungsi yang dilindungi di sini
    
    _setelahNonReentrant();
}

}

Teknik ini mencegah reentrancy lintas kontrak dengan mempertahankan registri status global yang melacak status eksekusi di berbagai kontrak dalam ekosistem Anda.

Praktik Terbaik Keamanan

Untuk memastikan perlindungan menyeluruh terhadap serangan reentrancy:

  1. Terapkan beberapa lapisan pertahanan: Gabungkan pola pemeriksaan-efek-interaksi dengan penjaga reentrancy untuk keamanan maksimum
  2. Lakukan pengujian menyeluruh: Gunakan alat khusus seperti Mythril untuk mendeteksi potensi kerentanan reentrancy
  3. Ikuti pola yang telah ditetapkan: Terapkan langkah-langkah keamanan secara konsisten di semua kontrak dalam proyek Anda
  4. Gunakan ketergantungan yang diaudit: Sertakan pustaka yang telah teruji dengan catatan keamanan yang terbukti
  5. Perbarui saldo terlebih dahulu: Selalu ubah status sebelum melakukan panggilan eksternal atau transfer

Dengan menerapkan mekanisme pertahanan ini, Anda dapat secara efektif melindungi kontrak pintar Anda dari salah satu vektor serangan paling berbahaya dalam keamanan blockchain.

ETH5.72%
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)