Supabase RLS: aplikasi bocor data tanpa satu pun error
Kasus yang paling sulit dijelaskan ke pemilik produk: tidak ada error di log, tidak ada halaman rusak, aplikasi terlihat normal — tetapi tabel orders, profiles, atau messages bisa dibaca siapa pun yang menemukan anon key di bundle JavaScript Anda. Kuncinya memang publik. Yang seharusnya menjaga data bukan kuncinya, tetapi aturan di database.
Di Supabase, aturan itu bernama Row Level Security (RLS). Kalau RLS tidak aktif, atau aktif tanpa policy yang benar, PostgREST akan dengan senang hati mengembalikan baris tabel kepada siapa pun yang meminta. Tidak ada yang "bocor" secara teknis: Anda memang membukanya.
RLS dalam satu paragraf
RLS adalah lapisan izin per baris di PostgreSQL. Setelah ALTER TABLE ... ENABLE ROW LEVEL SECURITY, tabel itu menolak semua akses sampai ada policy yang mengizinkan, misalnya "pengguna hanya boleh membaca baris di mana user_id = auth.uid()". Prinsipnya sederhana dan tidak bisa ditawar: tanpa policy, tidak ada data. Yang sering terjadi justru kebalikannya — RLS diaktifkan, policy dibuat terlalu longgar, dan hasilnya sama saja dengan RLS mati.
Tiga kesalahan yang paling sering
- Tabel dibuat tanpa RLS. Migrasi hanya berisi
CREATE TABLEdanGRANT. Karena tabel baru tidak otomatis mengaktifkan RLS, seluruh isinya dapat dibaca dengan anon key. Ini kesalahan paling umum pada proyek yang dibangun cepat dengan bantuan AI: skema jadi, keamanan baris tidak ikut disebut di prompt. - RLS aktif, tapi tidak ada policy. Terlihat aman di dashboard ("RLS: enabled"), tetapi aplikasi Anda sendiri jadi tidak bisa membaca apa pun — lalu "diperbaiki" dengan menambahkan policy longgar, bukan policy yang benar.
- Policy permisif:
USING (true)atauWITH CHECK (true). RLS menyala, ada policy, dan semua baris terbuka untuk semua orang. Sama terbukanya dengan RLS mati, tapi jauh lebih sulit dilihat karena dashboard menampilkan "aman".
allow read, write: if true, atau aturan tulis yang tidak memeriksa request.auth. Efeknya identik — data terbuka, aplikasi tetap berjalan normal.Cara memeriksa dalam 3 menit
- Cek status RLS di SQL Editor. Jalankan query ke
pg_tables/pg_classuntuk melihat tabel mana di skemapublicyangrowsecurity = false. Tabel baru yang tidak muncul di daftar migrasi Anda adalah tersangka pertama. - Baca policy-nya, bukan hanya jumlahnya.
SELECT * FROM pg_policies. CariUSING (true),WITH CHECK (true), atau policy yang tidak menyebutauth.uid()sama sekali. - Uji sebagai anonim. Dengan anon key (bukan service role), coba
selectke tabel paling sensitif. Kalau baris kembali, itu bukti — bukan dugaan.
Yang diperiksa AISG untuk lapisan ini
Sensor cloud-rules membaca berkas aturan di repo Anda — bukan database produksi — lalu menandai pola yang berbahaya:
supabase/migrations/*.sql: AISG-RLS-001 tabel tanpaENABLE ROW LEVEL SECURITY(atau RLS di-DISABLE), AISG-RLS-002 RLS aktif tetapi tidak adaCREATE POLICYuntuk tabel itu, AISG-RLS-003 policy permisifUSING (true)/WITH CHECK (true).firestore.rules,storage.rules,database.rules.json: AISG-FBR-001 aturan wildcardif true(tulis = high, baca = medium), AISG-FBR-002 aturan tulis tanparequest.auth.
Sensor ini berjalan lokal di komputer Anda, tanpa jaringan, dan termasuk dalam plan Community yang gratis. Hasilnya kandidat — belum bukti eksploitasi. Artinya: temuan ini harus Anda konfirmasi dengan uji anonim di atas sebelum dianggap benar.
Batas yang tidak boleh ditutupi
- Pembacaan statis tidak memahami policy dinamis yang bergantung pada fungsi, klaim JWT kustom, atau aturan bisnis berlapis. Bisa ada temuan yang salah, dan bisa ada kebocoran yang lolos.
- Kalau RLS Anda diatur manual di dashboard (bukan lewat migrasi), sensor tidak melihatnya — karena tidak ada berkas untuk dibaca. Itu justru alasan untuk memindahkan aturan ke migrasi: bisa di-review, bisa diuji, dan bisa dipindai.
- Satu temuan yang belum diverifikasi manusia tetap satu indikasi. AISG tidak memvalidasi dan tidak memitigasi; keputusan tetap di tangan Anda.
Tiga langkah hari ini: pastikan setiap tabel punya RLS, pastikan policy-nya menyebut pemilik data, lalu uji dengan anon key. Kalau ada yang terbuka, tutup sebelum ada yang menemukannya lebih dulu.