Apa Itu SQL Injection dan Cara Menutup Celahnya
SQL injection terjadi saat input pengunjung ikut dijalankan sebagai perintah database. Bandingkan cara menutupnya, dari prepared statement sampai WAF.
Jawaban singkat
SQL injection adalah celah ketika input pengguna disambung langsung ke perintah SQL sehingga penyerang bisa membaca, mengubah, atau menghapus data. Cara utama menutupnya prepared statement (query berparameter), dibantu validasi input, hak akses database minimal, dan WAF sebagai lapisan tambahan.
Daftar isi
SQL injection adalah celah keamanan yang muncul ketika aplikasi menyusun perintah database dengan menyambungkan teks dari pengunjung begitu saja. Penyerang lalu mengisi kolom formulir, parameter URL, atau cookie dengan potongan perintah SQL, dan database menjalankannya seolah-olah perintah itu ditulis oleh programmer.
Yang dipertaruhkan adalah isi database itu sendiri: data pelanggan, pesanan, password yang di-hash, sampai akun admin. Celah ini sudah dikenal puluhan tahun dan tetap masuk kategori Injection (A05) di OWASP Top 10 edisi 2025. Kabar baiknya, cara menutupnya sudah sangat jelas. Masalahnya, banyak yang memilih cara yang terasa cepat, padahal tidak menutup celahnya.
Bagaimana SQL injection terjadi?
Bayangkan halaman pencarian pesanan yang ditulis seperti ini di PHP:
$sql = "SELECT * FROM pesanan WHERE kode = '" . $_GET['kode'] . "'";
Bila pengunjung mengisi INV001, perintahnya wajar. Tapi bila yang diisi adalah ' OR '1'='1, perintah yang sampai ke database menjadi:
SELECT * FROM pesanan WHERE kode = '' OR '1'='1'
Kondisi '1'='1' selalu benar, sehingga seluruh pesanan ikut tampil. Itu contoh paling sederhana. Dengan teknik lain, penyerang bisa menggabungkan hasil dari tabel lain, mengubah data, atau menebak isi database sedikit demi sedikit lewat perbedaan waktu respons, bahkan saat halaman tidak menampilkan hasil query sama sekali.
Di mana celah ini biasa ditemukan?
- Kode buatan sendiri yang menyusun query dengan penyambungan string.
- Plugin atau modul pihak ketiga yang belum diperbarui. Di WordPress, celah SQL injection pada plugin cukup sering muncul di daftar kerentanan.
- Fitur pencarian, filter, urutan (
ORDER BY), dan halaman laporan yang menerima parameter dari URL. - API yang menerima JSON lalu memasukkan nilainya ke query tanpa parameter.
Cara menutup celah, mana yang benar-benar efektif?
Ada beberapa cara yang sering disebut. Tidak semuanya setara.
| Cara | Seberapa efektif | Catatan |
|---|---|---|
| Prepared statement / query berparameter | Sangat efektif, cara utama | Data dan perintah dikirim terpisah ke database |
| ORM atau query builder framework | Efektif bila dipakai sesuai aturan | Masih rawan bila memakai fungsi "raw query" sembarangan |
| Daftar putih (allowlist) untuk nama kolom dan arah urutan | Wajib untuk bagian yang tidak bisa diparameterkan | Misalnya ORDER BY dan nama tabel |
| Validasi input (tipe, panjang, format) | Membantu, bukan pengganti | Menolak nilai aneh sejak awal |
| Escaping manual | Rapuh | Mudah terlewat satu tempat, tergantung charset |
| Hak akses database minimal | Mengurangi dampak | Tidak mencegah, tapi membatasi kerusakan |
| WAF | Lapisan tambahan | Bisa dilewati dengan variasi payload |
Prepared statement
Dengan prepared statement, kamu mengirim kerangka perintah dengan penanda, lalu nilai dikirim terpisah. Database tahu persis mana perintah dan mana data, sehingga isi kolom tidak pernah bisa berubah menjadi perintah. Contoh dengan PDO di PHP:
$stmt = $pdo->prepare('SELECT * FROM pesanan WHERE kode = ?');
$stmt->execute([$_GET['kode']]);
$hasil = $stmt->fetchAll();
Di WordPress, gunakan $wpdb->prepare() dengan penanda %s untuk teks, %d untuk bilangan bulat, dan %f untuk desimal:
$hasil = $wpdb->get_results(
$wpdb->prepare( "SELECT * FROM {$wpdb->prefix}pesanan WHERE kode = %s", $kode )
);
Bagian yang tidak bisa diparameterkan
Penanda hanya berlaku untuk nilai, bukan untuk nama kolom, nama tabel, atau kata ASC/DESC. Untuk fitur "urutkan berdasarkan", cocokkan input dengan daftar yang diizinkan:
$kolom = in_array($_GET['urut'], ['tanggal', 'total'], true) ? $_GET['urut'] : 'tanggal';
Dengan begitu, apa pun yang dikirim pengunjung, yang masuk ke query hanya salah satu nilai yang sudah kamu tetapkan.
Kapan cukup memperbaiki kode, kapan perlu WAF?
Perbaikan kode selalu wajib. Kalau aplikasimu buatan sendiri dan kode sumbernya kamu pegang, langkah paling tepat adalah mencari semua query yang menyambung string, menggantinya dengan prepared statement, lalu menambahkan allowlist untuk bagian dinamis. Setelah itu, beri user database hak seminimal mungkin. Aplikasi yang hanya perlu membaca dan menulis data tidak butuh hak DROP atau GRANT.
WAF masuk akal saat kamu tidak bisa segera memperbaiki kode. Contohnya, plugin pihak ketiga yang celahnya sudah diumumkan tapi pembuatnya belum merilis perbaikan, aplikasi warisan yang tidak ada lagi programmernya, atau masa transisi sebelum perbaikan selesai diuji. Aturan seperti OWASP CRS di ModSecurity bisa menahan banyak pola serangan umum. Tapi anggap WAF sebagai perban, bukan jahitan. Penjelasan kapan WAF dibutuhkan ada di apa itu WAF.
Untuk website berbasis CMS seperti WordPress, prioritasnya adalah update. Sebagian besar celah SQL injection di situs seperti ini datang dari plugin, dan perbaikannya datang dari pembuat plugin. Hapus plugin yang tidak dipakai dan ganti plugin yang sudah lama ditinggalkan pembuatnya.
Bagaimana memeriksa apakah websitemu rentan?
Untuk kode sendiri, mulailah dengan meninjau kode: cari pola penyambungan string di dekat fungsi query. Lalu uji dengan pemindai kerentanan di lingkungan uji, misalnya seperti dijelaskan di cara scan kerentanan dengan OWASP ZAP. Hanya pindai website milikmu sendiri atau yang sudah memberi izin tertulis. Untuk aplikasi yang menyimpan data penting, uji penetrasi oleh pihak yang kompeten akan menemukan hal yang terlewat oleh pemindai otomatis.
Pantau juga log. Permintaan dengan potongan seperti UNION SELECT, ' OR, atau SLEEP( di parameter URL menandakan ada yang sedang mencoba. Itu belum tentu berhasil, tapi jadi pengingat untuk memeriksa bagian aplikasi yang dituju.
Pertanyaan yang sering diajukan
Apakah memakai framework modern otomatis aman dari SQL injection?
Sebagian besar aman bila memakai ORM atau query builder sesuai dokumentasi. Celah tetap bisa muncul saat programmer memakai fungsi raw query dengan menyambung input pengguna.
Apakah stored procedure mencegah SQL injection?
Hanya bila di dalamnya tidak ada SQL dinamis yang menyambung input. Stored procedure yang menyusun query dari string tetap rentan.
Apa dampak paling berat dari SQL injection?
Bocornya seluruh isi database, perubahan atau penghapusan data, dan pada konfigurasi tertentu, penyerang bisa membaca file di server. Karena itu hak akses user database perlu dibatasi.
Apakah validasi di JavaScript browser cukup?
Tidak. Validasi di browser mudah dilewati karena penyerang bisa mengirim permintaan langsung ke server. Validasi dan query berparameter harus ada di sisi server.


