Mengamankan Data Anda: Panduan Lengkap Implementasi Row-Level Security (RLS) di Supabase untuk Pengguna dan Admin
Keamanan data adalah hal yang paling utama dalam setiap aplikasi. Supabase, sebagai alternatif open-source untuk Firebase, menyediakan fitur ampuh yang disebut Row-Level Security (RLS). RLS memungkinkan Anda mengontrol akses ke baris data berdasarkan atribut pengguna. Artikel ini akan memandu Anda melalui proses penerapan RLS di Supabase untuk membedakan akses antara pengguna biasa dan administrator, memastikan bahwa data sensitif tetap terlindungi dan hanya dapat diakses oleh pihak yang berwenang.
Mengapa Row-Level Security Penting?
Sebelum kita masuk ke implementasi, mari pahami mengapa RLS sangat penting:
- Kontrol Akses Granular: RLS memungkinkan Anda mendefinisikan kebijakan yang menentukan pengguna mana yang dapat melihat, menyisipkan, memperbarui, atau menghapus baris data tertentu.
- Pencegahan Kebocoran Data: Dengan membatasi akses data berdasarkan peran atau atribut pengguna, RLS membantu mencegah kebocoran data dan akses yang tidak sah.
- Kepatuhan Regulasi: RLS membantu Anda memenuhi persyaratan kepatuhan terhadap regulasi privasi data seperti GDPR dan HIPAA.
- Keamanan yang Ditingkatkan: RLS menambahkan lapisan keamanan tambahan ke aplikasi Anda, melindungi data Anda bahkan jika terjadi kerentanan di lapisan aplikasi.
Prasyarat
Sebelum memulai, pastikan Anda memiliki hal berikut:
- Akun Supabase: Anda memerlukan akun Supabase. Jika Anda belum memilikinya, daftarlah di Supabase.
- Proyek Supabase: Buat proyek Supabase baru atau gunakan proyek yang sudah ada.
- Pengetahuan Dasar SQL: Pemahaman dasar tentang SQL diperlukan untuk mendefinisikan kebijakan RLS.
- Pengetahuan Autentikasi Supabase: Pahami bagaimana autentikasi pengguna bekerja di Supabase.
Langkah 1: Membuat Tabel Contoh
Mari buat tabel contoh bernama products
untuk mendemonstrasikan RLS. Tabel ini akan menyimpan informasi tentang produk, dan kita akan mengontrol akses ke produk berdasarkan pemiliknya (user_id).
CREATE TABLE products (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
description TEXT,
price DECIMAL NOT NULL,
user_id UUID NOT NULL REFERENCES auth.users(id),
created_at TIMESTAMP WITH TIME ZONE DEFAULT timezone('utc'::text, now())
);
Penjelasan kolom:
id
: ID unik produk (UUID).name
: Nama produk.description
: Deskripsi produk.price
: Harga produk.user_id
: ID pengguna yang memiliki produk (foreign key ke tabelauth.users
).created_at
: Tanggal dan waktu pembuatan produk.
Sekarang, mari masukkan beberapa data contoh:
INSERT INTO products (name, description, price, user_id)
VALUES
('Laptop', 'Laptop high-performance', 1200.00, 'user_id_1'),
('Smartphone', 'Smartphone flagship', 800.00, 'user_id_1'),
('Tablet', 'Tablet Android', 300.00, 'user_id_2');
Catatan: Ganti 'user_id_1'
dan 'user_id_2'
dengan ID pengguna yang sebenarnya dari tabel auth.users
Anda. Anda dapat menemukan ID pengguna di dashboard Supabase pada bagian Authentication -> Users.
Langkah 2: Mengaktifkan Row-Level Security
Secara default, RLS dinonaktifkan pada tabel baru. Kita perlu mengaktifkannya secara eksplisit. Buka SQL Editor di dashboard Supabase dan jalankan perintah berikut:
ALTER TABLE products ENABLE ROW LEVEL SECURITY;
Setelah menjalankan perintah ini, RLS diaktifkan untuk tabel products
. Namun, saat ini tidak ada kebijakan yang diterapkan, jadi semua orang masih dapat mengakses semua data. Mari kita definisikan kebijakan.
Langkah 3: Membuat Kebijakan RLS untuk Pengguna
Kita akan membuat kebijakan yang memungkinkan pengguna hanya melihat, menyisipkan, memperbarui, dan menghapus produk yang mereka miliki. Ini berarti pengguna hanya dapat berinteraksi dengan baris di mana user_id
sesuai dengan ID pengguna mereka saat ini.
- Kebijakan SELECT (Melihat): Izinkan pengguna untuk melihat produk mereka sendiri.
- Kebijakan INSERT (Menyisipkan): Izinkan pengguna untuk menyisipkan produk baru dengan ID pengguna mereka sendiri.
- Kebijakan UPDATE (Memperbarui): Izinkan pengguna untuk memperbarui produk mereka sendiri.
- Kebijakan DELETE (Menghapus): Izinkan pengguna untuk menghapus produk mereka sendiri.
CREATE POLICY "Users can view their own products"
ON products
FOR SELECT
USING (auth.uid() = user_id);
Kebijakan ini memungkinkan pengguna memilih (melihat) baris dari tabel products
hanya jika ID pengguna mereka (auth.uid()
) sama dengan nilai di kolom user_id
.
CREATE POLICY "Users can insert their own products"
ON products
FOR INSERT
WITH CHECK (auth.uid() = user_id);
Kebijakan ini memungkinkan pengguna menyisipkan baris baru ke dalam tabel products
, tetapi hanya jika ID pengguna mereka (auth.uid()
) sama dengan nilai di kolom user_id
. WITH CHECK
memastikan bahwa nilai user_id
di baris baru selalu sesuai dengan ID pengguna yang melakukan penyisipan.
CREATE POLICY "Users can update their own products"
ON products
FOR UPDATE
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
Kebijakan ini memungkinkan pengguna memperbarui baris yang ada dalam tabel products
, tetapi hanya jika ID pengguna mereka (auth.uid()
) sama dengan nilai di kolom user_id
. USING
menentukan baris mana yang dapat diperbarui, dan WITH CHECK
memastikan bahwa nilai user_id
tidak dapat diubah ke nilai yang berbeda.
CREATE POLICY "Users can delete their own products"
ON products
FOR DELETE
USING (auth.uid() = user_id);
Kebijakan ini memungkinkan pengguna menghapus baris dari tabel products
, tetapi hanya jika ID pengguna mereka (auth.uid()
) sama dengan nilai di kolom user_id
.
Langkah 4: Membuat Kebijakan RLS untuk Admin
Kita akan membuat kebijakan yang memungkinkan administrator untuk mengakses semua data dalam tabel products
. Untuk melakukan ini, kita perlu mekanisme untuk membedakan antara pengguna biasa dan administrator. Salah satu caranya adalah dengan menggunakan peran (role) di Supabase Auth.
Menambahkan Peran Admin
Pertama, kita perlu menambahkan kolom ke tabel auth.users
untuk menyimpan informasi peran pengguna. Supabase menyediakan fungsi supabase.add_claims()
untuk melakukan ini. Namun, secara langsung memodifikasi tabel auth.users
tidak disarankan. Sebaiknya, buat tabel profil terpisah yang memiliki referensi ke tabel auth.users
. Ini memberikan fleksibilitas lebih dan menjaga struktur tabel autentikasi tetap bersih.
CREATE TABLE profiles (
id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
email TEXT UNIQUE,
full_name TEXT,
avatar_url TEXT,
role TEXT DEFAULT 'user'
);
-- Trigger untuk membuat profil baru secara otomatis saat user baru mendaftar
CREATE OR REPLACE FUNCTION public.handle_new_user()
RETURNS TRIGGER AS $$
BEGIN
INSERT INTO public.profiles (id, email)
VALUES (NEW.id, NEW.email);
RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
CREATE TRIGGER on_auth_user_created
AFTER INSERT ON auth.users
FOR EACH ROW
EXECUTE PROCEDURE public.handle_new_user();
Penjelasan:
profiles
: Tabel yang menyimpan informasi profil pengguna, termasuk peran (role
).id
: Foreign key ke tabelauth.users
.email
: Email pengguna (diambil dariauth.users
).full_name
,avatar_url
: Kolom tambahan untuk informasi profil.role
: Peran pengguna (default adalah ‘user’).- Trigger
handle_new_user
: Secara otomatis membuat entry baru di tabelprofiles
ketika ada user baru yang mendaftar. Ini membantu kita untuk memiliki data profil yang konsisten.
Sekarang, kita dapat memperbarui peran pengguna menjadi ‘admin’ jika diperlukan. Anda dapat melakukan ini melalui SQL Editor atau melalui kode aplikasi Anda.
UPDATE profiles SET role = 'admin' WHERE id = 'user_id_admin'; -- Ganti 'user_id_admin' dengan ID pengguna admin yang sebenarnya
Membuat Kebijakan RLS untuk Admin
Sekarang kita memiliki peran admin, kita dapat membuat kebijakan RLS yang memberikan akses penuh kepada admin.
- Kebijakan SELECT (Melihat) untuk Admin: Izinkan admin untuk melihat semua produk.
- Kebijakan INSERT (Menyisipkan) untuk Admin: Izinkan admin untuk menyisipkan produk baru.
- Kebijakan UPDATE (Memperbarui) untuk Admin: Izinkan admin untuk memperbarui produk apa pun.
- Kebijakan DELETE (Menghapus) untuk Admin: Izinkan admin untuk menghapus produk apa pun.
CREATE POLICY "Admins can view all products"
ON products
FOR SELECT
USING (EXISTS (SELECT 1 FROM profiles WHERE profiles.id = auth.uid() AND profiles.role = 'admin'));
Kebijakan ini menggunakan subquery EXISTS
untuk memeriksa apakah pengguna saat ini memiliki peran ‘admin’ di tabel profiles
. Jika pengguna adalah admin, mereka diizinkan untuk memilih (melihat) semua baris dari tabel products
.
CREATE POLICY "Admins can insert new products"
ON products
FOR INSERT
WITH CHECK (EXISTS (SELECT 1 FROM profiles WHERE profiles.id = auth.uid() AND profiles.role = 'admin'));
Mirip dengan kebijakan SELECT, kebijakan ini menggunakan subquery EXISTS
untuk memeriksa peran admin. Jika pengguna adalah admin, mereka dapat menyisipkan baris baru ke dalam tabel products
.
CREATE POLICY "Admins can update any product"
ON products
FOR UPDATE
USING (EXISTS (SELECT 1 FROM profiles WHERE profiles.id = auth.uid() AND profiles.role = 'admin'))
WITH CHECK (EXISTS (SELECT 1 FROM profiles WHERE profiles.id = auth.uid() AND profiles.role = 'admin'));
Kebijakan ini memungkinkan admin untuk memperbarui baris apa pun. USING
dan WITH CHECK
keduanya menggunakan subquery EXISTS
untuk memastikan bahwa hanya admin yang dapat melakukan pembaruan.
CREATE POLICY "Admins can delete any product"
ON products
FOR DELETE
USING (EXISTS (SELECT 1 FROM profiles WHERE profiles.id = auth.uid() AND profiles.role = 'admin'));
Kebijakan ini memungkinkan admin untuk menghapus baris apa pun dari tabel products
.
Langkah 5: Menguji Kebijakan RLS
Setelah Anda mendefinisikan kebijakan RLS, penting untuk mengujinya secara menyeluruh untuk memastikan bahwa mereka berfungsi seperti yang diharapkan. Berikut adalah beberapa cara untuk menguji kebijakan RLS Anda:
- Menggunakan SQL Editor: Anda dapat menggunakan SQL Editor di dashboard Supabase untuk menjalankan kueri sebagai pengguna yang berbeda. Untuk melakukan ini, Anda perlu mengatur variabel
request.jwt.claim.sub
ke ID pengguna yang ingin Anda simulasikan.-- Atur variabel request.jwt.claim.sub ke ID pengguna set request.jwt.claim.sub = 'user_id_1'; -- Jalankan kueri SELECT SELECT * FROM products;
Ganti
'user_id_1'
dengan ID pengguna yang sebenarnya. Setelah menjalankan kueri, Anda akan melihat hanya produk yang dimiliki oleh pengguna tersebut.-- Atur variabel request.jwt.claim.sub ke ID admin set request.jwt.claim.sub = 'user_id_admin'; -- Jalankan kueri SELECT SELECT * FROM products;
Ganti
'user_id_admin'
dengan ID pengguna admin yang sebenarnya. Setelah menjalankan kueri, Anda akan melihat semua produk. - Menggunakan Aplikasi Anda: Integrasikan autentikasi Supabase ke dalam aplikasi Anda dan uji kebijakan RLS dari sisi klien. Masuk sebagai pengguna yang berbeda dan coba melakukan operasi yang berbeda (SELECT, INSERT, UPDATE, DELETE) pada tabel
products
. Pastikan bahwa pengguna hanya dapat melakukan operasi pada data yang mereka miliki dan bahwa admin dapat melakukan operasi pada semua data.
Praktik Terbaik untuk Row-Level Security
Berikut adalah beberapa praktik terbaik untuk menerapkan RLS di Supabase:
- Minimal Privilege: Berikan hanya hak akses yang diperlukan kepada pengguna. Hindari memberikan akses yang tidak perlu ke data.
- Audit dan Pantau: Secara teratur audit kebijakan RLS Anda dan pantau aktivitas pengguna untuk memastikan keamanan data.
- Dokumentasikan Kebijakan: Dokumentasikan semua kebijakan RLS Anda dengan jelas dan ringkas. Ini akan membantu Anda memahami dan memelihara kebijakan Anda di masa mendatang.
- Uji Secara Menyeluruh: Uji kebijakan RLS Anda secara menyeluruh sebelum menerapkan ke produksi. Pastikan bahwa mereka berfungsi seperti yang diharapkan dan tidak ada celah keamanan.
- Gunakan Fungsi
auth.uid()
dengan Hati-hati: Fungsiauth.uid()
bergantung pada klaim JWT yang dikirim oleh klien. Pastikan bahwa klien Anda aman dan tidak dapat dimanipulasi untuk mengirim klaim palsu. - Pertimbangkan Performa: Kebijakan RLS yang kompleks dapat memengaruhi performa database Anda. Optimalkan kebijakan Anda untuk meminimalkan dampak performa.
Contoh Kasus Penggunaan Lainnya
Berikut adalah beberapa contoh kasus penggunaan lain untuk RLS:
- Aplikasi SaaS Multi-Tenant: Memastikan bahwa setiap penyewa (tenant) hanya dapat mengakses data mereka sendiri.
- Aplikasi Kesehatan: Memastikan bahwa pasien hanya dapat mengakses catatan kesehatan mereka sendiri dan bahwa dokter memiliki akses ke catatan pasien yang mereka rawat.
- Aplikasi Keuangan: Memastikan bahwa pengguna hanya dapat mengakses akun keuangan mereka sendiri dan bahwa staf keuangan memiliki akses ke semua akun.
Kesimpulan
Row-Level Security adalah fitur yang ampuh di Supabase yang memungkinkan Anda mengontrol akses ke data Anda dengan granularitas yang tinggi. Dengan menerapkan kebijakan RLS yang tepat, Anda dapat memastikan bahwa data sensitif Anda tetap terlindungi dan hanya dapat diakses oleh pihak yang berwenang. Dalam panduan ini, kita telah membahas cara menerapkan RLS untuk membedakan akses antara pengguna biasa dan administrator. Ingatlah untuk selalu menguji kebijakan RLS Anda secara menyeluruh dan mengikuti praktik terbaik untuk memastikan keamanan data Anda.
Dengan kombinasi autentikasi Supabase dan Row-Level Security, Anda dapat membangun aplikasi yang aman dan andal dengan mudah.
“`