Aturan akses Supabase dan Firebase (CWE-862): bagaimana Row Level Security yang hilang mengekspos data dan cara memperbaikinya
Aplikasi Supabase dan Firebase membiarkan browser mengkueri database secara langsung dengan kunci publik, sehingga satu-satunya penghalang antara pengunjung dan setiap baris adalah Row Level Security atau Security Rules. Tabel yang dibuat lewat SQL tanpa enable row level security, atau aturan Firestore yang dibiarkan allow read, write: if true, bisa dibaca dan diubah siapa saja. Bagaimana kebijakan USING dan WITH CHECK membatasi baris ke pengguna yang sedang login, dengan contoh migrasi PostgreSQL.
Penjelasan Sederhana (ELI5)
Sebuah gedung kantor memberi setiap tamu kartu lobi yang sama, dicetak di balik brosur. Itu tidak masalah selama pintu setiap lantai memeriksa siapa Anda sebelum terbuka. Satu lantai disiapkan terburu-buru dan pintunya tidak punya pembaca kartu sama sekali, sehingga kartu lobi bisa membukanya dan siapa pun bisa masuk membaca berkas. Kunci anon Supabase adalah kartu lobi itu: memang dimaksudkan publik. Row Level Security adalah pembaca kartu di pintu setiap lantai. Perbaikannya adalah memasang pembaca di setiap pintu dan memberitahunya dengan tepat berkas siapa yang boleh dilihat tiap orang.
Konsep Kunci & Istilah
- Kunci anon publik
- Kunci
anonSupabase dan konfigurasi web Firebase ikut di dalam bundle aplikasi, dan siapa pun bisa menyalinnya. Keduanya mengidentifikasi proyek, bukan pengguna, jadi bukan rahasia dan tidak bisa melindungi data sendirian. - Row Level Security (RLS)
- Fitur PostgreSQL yang menyaring setiap kueri melalui kebijakan. Table Editor Supabase mengaktifkannya untuk tabel baru, tetapi tabel yang dibuat lewat SQL atau migrasi dimulai dengan RLS mati, dan hak akses default lalu membiarkan
anondanauthenticatedmembaca dan mengubah setiap baris. - USING dan WITH CHECK
USINGmenentukan baris yang sudah ada mana yang boleh dilihat, diubah, atau dihapus pengguna;WITH CHECKmenentukan baris baru atau yang diubah mana yang boleh ia tulis. Kebijakan yang hanya punyaUSINGpada insert atau update membiarkan pengguna menulis baris untuk organisasi lain.- Kunci service_role
- Kunci
service_rolemelewati RLS sepenuhnya. Tempatnya hanya di server dan edge function, tidak pernah di browser atau aplikasi seluler. - Aturan Firestore mode uji
- Proyek yang dimulai dalam mode uji mendapat aturan yang mengizinkan semua baca dan tulis hingga tanggal tertentu, dan banyak yang kemudian diubah menjadi
allow read, write: if true. Aturan harus mencocokkanrequest.auth.uiddengan field pemilik dokumen.
Alur Serangan Langkah demi Langkah
Penyerang membuka aplikasi
URL Supabase dan kunci anon ada di bundle JavaScript atau paket aplikasi seluler.
Ia mengkueri tabel secara langsung
Ia memanggil endpoint REST untuk invoices hanya dengan kunci anon, melewati layar aplikasi. Request ke proyek uji dengan kunci anon dan tanpa sesi pengguna menggambarkan kasus ini.
Database mengembalikan setiap baris
RLS mati dan anon punya hak select, sehingga PostgreSQL tidak menyaring apa pun dan mengembalikan invoice semua organisasi.
Data bocor atau diubah
Jika update dan delete juga diberikan, penyerang bisa mengubah atau menghapus record. CVE-2025-48757 diterbitkan pada 2025 setelah peneliti menemukan RLS yang hilang mengekspos data di lebih dari 170 aplikasi yang dibuat oleh satu pembuat aplikasi berbasis AI.
Kode Sumber: Rentan vs Aman
-- migration.sql: RLS tidak pernah diaktifkan di tabel ini
create table public.invoices (
id bigint generated always as identity primary key,
org_id uuid not null references public.orgs (id),
customer text not null,
amount_cents bigint not null
);
-- kunci anon publik bisa membaca, mengubah, dan menghapus setiap baris
grant select, insert, update, delete on public.invoices to anon, authenticated;
-- migration.sql: RLS aktif, hak anon dicabut, satu kebijakan per perintah
create table public.invoices (
id bigint generated always as identity primary key,
org_id uuid not null references public.orgs (id),
customer text not null,
amount_cents bigint not null
);
-- setelah RLS aktif, tanpa kebijakan berarti tanpa akses
alter table public.invoices enable row level security;
revoke all on public.invoices from anon;
-- pengguna hanya melihat invoice organisasi tempat mereka bergabung
create policy "members read their org's invoices"
on public.invoices for select to authenticated
using (org_id in (select org_id from public.memberships where user_id = (select auth.uid())));
-- baris baru harus milik salah satu organisasi pengguna; tidak ada kebijakan update atau delete
create policy "members add invoices to their org"
on public.invoices for insert to authenticated
with check (org_id in (select org_id from public.memberships where user_id = (select auth.uid())));
Daftar Periksa Penguatan Sistem Rekayasa
- Aktifkan RLS di setiap tabel pada skema yang terekspos, dan gagalkan CI ketika
select tablename from pg_tables where schemaname = 'public' and not rowsecuritymengembalikan baris apa pun. - Tulis satu kebijakan per perintah, dibatasi
to authenticated, denganUSINGuntuk pembacaan danWITH CHECKuntuk insert dan update; jangan buat kebijakan update dan delete yang tidak diperlukan. - Cabut hak tabel dari
anonjika pengguna anonim tidak punya alasan membaca, dan simpan kunciservice_rolehanya di lingkungan server. - Aktifkan RLS juga pada tabel yang dibaca kebijakan Anda, seperti
memberships, agar pengguna tidak bisa menambahkan dirinya ke organisasi lain. - Uji dengan dua pengguna uji: pengguna A harus mendapat nol baris untuk organisasi pengguna B; untuk Firestore, jalankan tes unit aturan di emulator dengan
@firebase/rules-unit-testing.