Supabase memudahkan pembuatan backend berbasis PostgreSQL, tetapi kemudahan tersebut tidak otomatis membuat aplikasi aman. Kebocoran biasanya terjadi karena secret disimpan di frontend, Row Level Security (RLS) tidak diaktifkan, policy terlalu longgar, atau bucket Storage dibuka tanpa batasan.

1. Pahami key yang boleh dan tidak boleh dibagikan

Supabase menyediakan URL project dan publishable key untuk dipakai aplikasi client. Publishable key memang dirancang agar dapat berada di browser, tetapi aksesnya tetap dibatasi oleh RLS dan policy.

Service role key tidak boleh masuk ke frontend, repository publik, log, screenshot, atau file yang dikirim ke pengguna. Key ini dapat melewati RLS dan hanya boleh digunakan di server terpercaya, seperti API route, server action, worker, atau job backend.

# .env.local
NEXT_PUBLIC_SUPABASE_URL=https://project-ref.supabase.co
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=your-publishable-key
SUPABASE_SERVICE_ROLE_KEY=server-only-secret

Gunakan prefix NEXT_PUBLIC_ hanya untuk nilai yang memang aman diekspos ke browser. Semua secret lain harus tanpa prefix tersebut dan dibaca hanya di server.

2. Aktifkan RLS di semua tabel yang dapat diakses client

RLS adalah lapisan pertahanan utama pada tabel PostgreSQL Supabase. Aktifkan RLS segera setelah tabel dibuat:

alter table public.profiles enable row level security;

alter table public.notes enable row level security;

Jangan menganggap nama tabel yang tidak diketahui pengguna sudah cukup sebagai keamanan. Jika API dapat menjangkau tabel, akses harus dikontrol dengan policy.

3. Buat policy berdasarkan identitas pengguna

Contoh berikut hanya mengizinkan pengguna membaca dan mengubah catatan miliknya sendiri. Kolom user_id harus menyimpan ID dari auth.users:

create policy "Users read their own notes"
on public.notes for select
to authenticated
using (auth.uid() = user_id);

create policy "Users create their own notes"
on public.notes for insert
to authenticated
with check (auth.uid() = user_id);

create policy "Users update their own notes"
on public.notes for update
to authenticated
using (auth.uid() = user_id)
with check (auth.uid() = user_id);

create policy "Users delete their own notes"
on public.notes for delete
to authenticated
using (auth.uid() = user_id);

Gunakan using untuk memeriksa baris yang boleh dibaca atau diubah, dan with check untuk memastikan data baru atau hasil perubahan tetap memenuhi aturan.

4. Hindari policy yang terlalu longgar

Policy seperti using (true) dapat membuat seluruh baris terbaca oleh siapa pun. Policy tersebut hanya layak untuk data yang memang bersifat publik, dan tetap harus dipasangkan dengan pembatasan operasi yang jelas.

Batasi role policy dengan to authenticated atau to anon secara sengaja. Jangan memberi akses anonim hanya karena ingin membuat proses development lebih mudah.

5. Pisahkan client browser dan server client

Client browser gunakan publishable key dan selalu bergantung pada RLS. Operasi administratif memakai service role key hanya di server:

// server-only code
import { createClient } from '@supabase/supabase-js';

const admin = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.SUPABASE_SERVICE_ROLE_KEY!,
  { auth: { autoRefreshToken: false, persistSession: false } }
);

Jangan mengimpor file server-only ke Client Component. Tambahkan validasi seperti server-only bila framework mendukungnya, dan lakukan pemeriksaan pada CI agar nama service role key tidak masuk bundle browser.

6. Amankan Supabase Storage

Bucket public berarti siapa pun yang memiliki URL dapat mengambil file. Untuk dokumen pribadi, gunakan bucket private dan signed URL dengan masa berlaku singkat.

Policy Storage sebaiknya memeriksa folder berdasarkan user ID. Contoh konsepnya:

create policy "Users upload into their own folder"
on storage.objects for insert
to authenticated
with check (
  bucket_id = 'private-files'
  and (storage.foldername(name))[1] = (select auth.uid()::text)
);

Batasi MIME type, ukuran file, ekstensi, dan nama file. Jangan menampilkan file upload secara langsung tanpa validasi karena file berbahaya dapat dimanfaatkan untuk serangan terhadap pengguna atau administrator.

7. Lindungi autentikasi dan session

Aktifkan verifikasi email bila aplikasi membutuhkan akun yang valid. Gunakan password policy yang layak, rate limit pada alur login, dan redirect URL yang hanya menunjuk ke domain resmi.

Jangan menyimpan access token di tempat yang mudah dibaca script pihak ketiga. Untuk aplikasi SSR, gunakan integrasi cookie resmi Supabase dan konfigurasi session sesuai framework. Selalu logout dan cabut session ketika akun dicurigai.

8. Kelola secret dan repository

Tambahkan .env* ke .gitignore, kecuali file contoh yang tidak berisi nilai rahasia. Jika key pernah ter-commit, menghapus file saja tidak cukup karena secret masih ada di riwayat Git.

git status
# pastikan file secret tidak terlacak
git check-ignore .env.local

Jika terjadi kebocoran, segera rotate key dari dashboard Supabase, perbarui secret di server dan CI/CD, lalu periksa log akses. Perlakukan semua secret yang pernah dipublikasikan sebagai sudah tidak aman.

9. Gunakan migration dan backup

Simpan perubahan schema dan policy dalam migration yang direview. Hindari mengubah database production secara manual tanpa catatan. Jadwalkan backup, uji proses restore, dan batasi siapa yang memiliki akses ke database password serta dashboard.

10. Checklist audit keamanan

  • Service role key hanya berada di server dan secret manager.
  • RLS aktif pada seluruh tabel yang terekspos API.
  • Setiap policy memiliki target role dan kondisi yang jelas.
  • Data pengguna memakai pemeriksaan auth.uid().
  • Bucket private digunakan untuk dokumen sensitif.
  • Signed URL memiliki masa berlaku terbatas.
  • Redirect URL dan CORS hanya mengizinkan domain yang diperlukan.
  • Secret tidak ada di Git, bundle browser, log, atau screenshot.
  • Key dapat di-rotate dan proses recovery sudah diuji.
  • Migration, backup, monitoring, dan review akses dilakukan rutin.

Kesimpulan

Supabase yang aman dibangun dengan prinsip sederhana: client hanya mendapat akses minimum, server menyimpan secret, dan database menentukan siapa boleh melakukan apa. Aktifkan RLS sejak awal, tulis policy berdasarkan identitas pengguna, amankan Storage, dan siapkan prosedur rotation jika terjadi kebocoran. Dengan pendekatan ini, Supabase dapat digunakan dengan cepat tanpa mengorbankan keamanan data.