Kerentanan Reentrancy: Cara Mengidentifikasi, Mengeksploitasi, dan Mencegahnya

Dalam dunia kontrak pintar, reentrancy dianggap sebagai salah satu celah paling berbahaya. Artikel ini akan membantu Anda tidak hanya memahami apa itu serangan reentrancy, tetapi juga cara melindunginya secara efektif. Dari teknik dasar hingga solusi tingkat lanjut, kita akan menjelajahi berbagai cara untuk melindungi seluruh proyek Anda.

Bagaimana Reentrancy Berfungsi: Mekanisme Serangan Dasar

Untuk memahami reentrancy, pertama-tama kita perlu memahami konsep dasar: sebuah kontrak pintar dapat memanggil kontrak lain, dan saat itu, kontrak kedua dapat memanggil kembali kontrak pertama saat masih dalam proses eksekusi.

Bayangkan Anda memiliki dua kontrak: ContractA menyimpan 10 Ether dan ContractB telah mengirimkan 1 Ether ke sana. Ketika ContractB memanggil fungsi penarikan, akan diperiksa apakah saldo cukup. Jika ya, Ether akan dikirim kembali ke ContractB. Pada titik ini, jika tidak ada langkah perlindungan yang tepat, inilah celah yang bisa dimanfaatkan penyerang.

Dalam serangan reentrancy tipikal, penyerang membutuhkan dua fungsi: attack() untuk memulai serangan, dan fallback() untuk melakukan panggilan kembali. Fungsi fallback adalah fungsi khusus dalam Solidity — tidak memiliki nama, tidak memiliki parameter, dan akan dipanggil otomatis setiap kali Ether dikirim ke kontrak tanpa data apa pun.

Langkah-langkah Melakukan Serangan Reentrancy

Ikuti proses serangan secara berurutan. Penyerang memanggil fungsi attack() dari kontraknya sendiri. Di dalam fungsi ini, ia akan memanggil fungsi withdraw() dari ContractA.

Ketika ContractA menerima panggilan ini, ia memeriksa apakah ContractB memiliki saldo lebih dari 0. Jika ya, Ether akan dikirim kembali ke ContractB, memicu fungsi fallback-nya. Pada saat ini, ContractA tersisa 9 Ether, tetapi yang lebih penting — saldo ContractB dalam catatan kontrak masih belum diperbarui ke 0.

Ini adalah masalah: fallback() memanggil withdraw() dari ContractA lagi. ContractA memeriksa kembali saldo ContractB — masih 1 Ether! Kenapa? Karena baris balance[msg.sender] = 0 belum pernah dieksekusi, karena berada setelah pengiriman Ether.

Proses ini berulang: panggilan withdraw → pemeriksaan saldo (masih > 0) → pengiriman Ether → fallback() dipicu → panggilan withdraw lagi… sampai seluruh Ether ContractA berhasil ditarik.

Analisis Kode: Ketika Reentrancy Menjadi Kenyataan

Contract EtherStore adalah contoh kontrak yang rentan terhadap serangan. Ia memiliki fungsi deposit() untuk menyimpan saldo dan withdrawAll() untuk menarik uang. Masalahnya terletak pada cara withdrawAll() diimplementasikan: memeriksa kondisi, mengirim Ether, lalu memperbarui saldo.

Kontrak Attack akan memanfaatkan celah ini. Dalam konstruktor, penyerang mengirimkan alamat EtherStore, sehingga dapat memanggil fungsi-fungsinya. Fungsi fallback dari kontrak Attack akan dipanggil setiap kali EtherStore mengirim Ether, dan di dalamnya akan terus memanggil kembali withdrawAll() selama masih ada Ether. Fungsi attack() memulai proses dengan mengirim 1 Ether pertama ke EtherStore untuk melewati pemeriksaan awal.

Hasilnya, seluruh dana EtherStore berhasil ditarik dalam satu transaksi.

Tiga Strategi Melindungi Kontrak dari Reentrancy

Untuk melindungi kontrak pintar, ada tiga tingkat perlindungan berbeda, dari dasar hingga lengkap.

Contoh noReentrant: Solusi Perlindungan Dasar

Metode paling sederhana adalah menggunakan modifier noReentrant(). Modifier adalah jenis fungsi khusus dalam Solidity yang memungkinkan Anda mengubah perilaku fungsi lain tanpa menulis ulang seluruhnya.

Ide sederhananya: saat sebuah fungsi dilindungi oleh noReentrant(), kontrak akan dikunci selama proses eksekusi. Setiap panggilan yang mencoba masuk kembali ke fungsi ini akan gagal karena variabel status kunci tidak mengizinkan. Hanya setelah fungsi selesai dan dibuka kunci, panggilan lain dapat berhasil.

Solusi ini sangat efektif untuk melindungi satu fungsi, tetapi tidak menangani kasus yang lebih kompleks.

Contoh Check-Effect-Interaction: Mencegah Reentrancy Multi-Fungsi

Teknik kedua yang lebih kuat: menerapkan pola Check-Effect-Interaction. Alih-alih melindungi satu fungsi, pola ini mengubah cara Anda menulis logika fungsi.

Prinsip utamanya adalah: periksa kondisi terlebih dahulu (Check), perbarui status segera setelahnya (Effect), lalu berinteraksi dengan kontrak eksternal (Interaction). Ini mencegah penyerang memanfaatkan dengan memanggil kembali, karena saat mereka memanggil lagi, saldo sudah diperbarui menjadi 0.

Alih-alih memperbarui balance[msg.sender] = 0 setelah mengirim Ether, pindahkan ke sebelum pengiriman. Dengan begitu, meskipun fallback() memanggil kembali, pemeriksaan akan selalu gagal karena saldo sudah 0.

Metode ini melindungi kontrak dari reentrancy secara menyeluruh, bahkan saat ada banyak fungsi penarikan uang berbeda.

GlobalReentrancyGuard: Perlindungan Menyeluruh di Seluruh Proyek

Untuk proyek yang kompleks dengan banyak kontrak yang berinteraksi, kita membutuhkan solusi yang lebih lengkap: GlobalReentrancyGuard.

Alih-alih mengunci di tingkat fungsi, solusi ini mengunci di tingkat seluruh proyek. Anda membuat kontrak terpisah yang menyimpan variabel status kunci bersama, dan semua kontrak lain dalam proyek merujuk ke sana.

Bayangkan skenario ini: penyerang memanggil fungsi dalam kontrak ScheduledTransfer. Setelah melewati pemeriksaan, ia mengirim Ether ke AttackTransfer. Fungsi fallback AttackTransfer diaktifkan dan berusaha memanggil kembali ScheduledTransfer. Tetapi karena GlobalReentrancyGuard telah mengunci status secara global, panggilan ini dicegah.

Metode ini sangat berguna untuk proyek besar dengan banyak kontrak, di mana reentrancy bisa terjadi antar kontrak yang berbeda.

Memilih Teknik yang Tepat untuk Proyek Anda

Pilihan strategi tergantung pada kompleksitas proyek. Jika kontrak Anda memiliki sedikit fungsi interaksi, noReentrant() sudah cukup efektif. Jika ada banyak fungsi penarikan, pola Check-Effect-Interaction adalah pilihan terbaik. Untuk proyek besar dengan banyak kontrak, GlobalReentrancyGuard memberikan perlindungan lengkap.

Tidak peduli apa yang Anda pilih, hal utama adalah memahami bagaimana reentrancy bekerja, agar Anda dapat mengenali dan mencegahnya secara proaktif.

Untuk pembaruan harian tentang keamanan kontrak pintar, memeriksa kode sumber, dan tren terbaru di bidang Web3, ikuti sumber daya mendalam tentang keamanan Solidity.

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
Tambahkan komentar
Tambahkan komentar
Tidak ada komentar
  • Sematkan