Supabase Row Level Security (RLS): kunci tabel-tabelmu
Supabase mengekspos tabel Postgres-mu langsung ke browser lewat Data API-nya, diautentikasi dengan anon (publishable) key yang publik. Policy Row Level Security (RLS) yang menentukan baris mana yang boleh dibaca atau diubah setiap user. Tabel di schema public tanpa RLS bisa dibaca — dan sering kali juga ditulis — oleh siapa saja yang membuka situsmu.
Apa itu
Setiap project Supabase punya anon (atau sb_publishable_) key yang memang dimaksudkan untuk ada di frontend. Key ini mengidentifikasi project-mu, bukan user-mu — siapa pun bisa menyalinnya dari bundle JavaScript-mu dan mengirim request ke https://<project>.supabase.co/rest/v1/ sendiri, melewati UI-mu sepenuhnya.
Row Level Security adalah fitur Postgres yang memasang policy pada sebuah tabel: kondisi SQL yang diperiksa untuk setiap baris pada setiap SELECT, INSERT, UPDATE, dan DELETE. Dengan RLS aktif tanpa policy, tidak ada yang bisa diakses lewat API. Setiap policy yang kamu tambahkan membuka tepat satu pintu — misalnya "user yang sudah login boleh membaca baris yang user_id-nya cocok dengan id mereka sendiri".
Kenapa penting
Policy yang hilang atau terlalu longgar adalah kebocoran data total, bukan bug kecil: email user, profil, pesanan, pesan pribadi — apa pun yang ada di tabel — bisa disedot dengan satu perintah curl. Tool coding AI sering membuat tabel lewat migrasi SQL, di mana RLS mati secara default, dan menghasilkan policy placeholder yang serba membolehkan seperti using (true) sekadar agar aplikasinya jalan.
Key service_role (sb_secret_) melewati RLS sepenuhnya. Kalau key ini sampai ke kode sisi klien, semua policy yang kamu tulis jadi tidak berarti — siapa pun yang memegangnya punya akses baca/tulis penuh ke database-mu.
Cara memperbaiki
Jalankan ini di SQL editor Supabase untuk menampilkan setiap tabel di schema public yang terekspos yang RLS-nya masih mati. Security Advisor di dashboard melaporkan masalah yang sama sebagai "RLS disabled in public".
select tablename
from pg_tables
where schemaname = 'public'
and rowsecurity = false;Nyalakan RLS untuk setiap tabel, lalu tambahkan satu policy per operasi. Batasi policy ke role authenticated dan bandingkan kolom pemilik baris dengan auth.uid(). Insert dan update butuh with check agar user tidak bisa menulis baris milik orang lain.
alter table public.profiles enable row level security;
create policy "Users read own profile"
on public.profiles for select
to authenticated
using ((select auth.uid()) = user_id);
create policy "Users update own profile"
on public.profiles for update
to authenticated
using ((select auth.uid()) = user_id)
with check ((select auth.uid()) = user_id);Ganti policy placeholder seperti using (true) dengan kondisi sungguhan. Jangan mendasarkan otorisasi pada user_metadata — user bisa mengubahnya sendiri; pakai app_metadata atau tabel role sebagai gantinya. View melewati RLS kecuali dibuat dengan security_invoker = true, dan storage bucket butuh policy sendiri di storage.objects.
Query API-mu persis seperti yang dilakukan penyerang: hanya dengan key publik dan tanpa sesi user. Kalau ini mengembalikan baris yang seharusnya tidak publik, policy-mu terlalu terbuka.
curl "https://<project>.supabase.co/rest/v1/profiles?select=*" \
-H "apikey: <anon-key>" \
-H "Authorization: Bearer <anon-key>"Pakai service_role (sb_secret_) key hanya di kode server seperti API route atau Edge Functions, jangan pernah di balik prefiks NEXT_PUBLIC_ atau VITE_. Kalau key ini pernah terkirim ke browser, rotasi di dashboard.
FAQ
Apakah aman kalau anon key Supabase-ku terlihat di browser?
Ya — anon (publishable) key memang dirancang untuk publik, tapi hanya kalau setiap tabel yang terekspos punya RLS aktif dengan policy yang benar. Key itu sendiri tidak memberi akses apa pun yang tidak diizinkan policy-mu.
Aku mengaktifkan RLS dan sekarang aplikasiku mengembalikan hasil kosong. Apakah rusak?
Itu tandanya RLS bekerja sesuai rancangan: tanpa policy, setiap baris ditolak. Tambahkan policy untuk operasi yang dibutuhkan aplikasimu, dibatasi ke user yang tepat, alih-alih mematikan RLS lagi.
Apakah AppSafe menguji policy RLS-ku?
AppSafe tidak login ke database-mu, jadi tidak bisa mengevaluasi policy-mu — pakai pengecekan SQL dan curl di atas. Tapi AppSafe memang memindai JavaScript-mu untuk mencari key Supabase. Kalau ada yang dilaporkan, decode bagian tengah JWT-nya dan periksa claim role: anon memang wajar ada di browser, service_role adalah kebocoran kritis.
Apakah aplikasimu terpengaruh?
AppSafe memeriksa ini dan puluhan masalah lain dalam satu pemindaian gratis.
Pindai aplikasiku gratisPanduan terkait
Firebase Security Rules: jangan biarkan database-mu terbuka
Konfigurasi Firebase-mu memang publik. Rules-mu yang menentukan siapa yang bisa melihat data.
API key terekspos di JavaScript sisi klien
Apa pun di bundle-mu bersifat publik. Perlakukan seperti itu.
Backup database yang terekspos: apa yang terjadi saat /backup.sql dapat dijangkau
Dump .sql yang dapat dijangkau sama dengan menyerahkan seluruh database Anda.