Lista e kontrollit RLS në Supabase për app AI
Çfarë është row level security në Supabase, si aktivizohet, policy-të që u duhen shumicës së aplikacioneve dhe kontrollet që bëjmë në çdo tabelë para lançimit.
- 19 gus 2026

Supabase i lejon shfletuesit të flasë drejtpërdrejt me bazën e të dhënave. Për këtë arsye e zgjedhin mjete AI si Lovable dhe Bolt, dhe po për këtë arsye row level security ka më shumë rëndësi në Supabase se pothuajse kudo tjetër. Shfletuesi mban një çelës, ndaj vetë baza e të dhënave duhet të jetë ajo që thotë jo.
Kjo është lista e kontrollit që përdorim në çdo projekt Supabase para lançimit, dhe gjëja e parë që hapim në një shpëtim. Shpjegon çfarë është RLS, si aktivizohet, policy-të që u duhen shumicës së aplikacioneve, rrugët nga të cilat rrjedhin të dhënat dhe si të provoni që funksionon.
Çfarë është row level security
Row level security (RLS) është një veçori e Postgres. Kur është aktive, çdo kërkesë ndaj një tabele filtrohet nga rregulla që quhen policy, dhe një rresht kthehet vetëm nëse një policy e lejon. Mendojeni një policy si një kusht WHERE që baza e të dhënave ia shton çdo kërkese, kushdo që ta ketë dërguar.
Supabase e ekspozon skemën public përmes një API që mund ta thërrasë kushdo që ka URL-në e projektit dhe çelësin anon. Të dyja ndodhen në kodin e frontend-it, pra janë publike. Pa RLS, ai API i kthen çdo rresht të çdo tabele kujtdo që e kërkon.
Hapi 1. Aktivizojeni në çdo tabelë
Tabelat e krijuara në editorin e tabelave të Supabase zakonisht e kanë RLS aktive. Ato të krijuara me SQL, në një migrim ose nga një mjet AI shpesh jo. Kontrollojini të gjitha, edhe ato të shtuara javën e kaluar.
-- Which tables in the public schema have RLS off?
select tablename
from pg_tables
where schemaname = 'public'
and rowsecurity = false;
-- Switch it on
alter table public.profiles enable row level security;Një tabelë me RLS aktive dhe pa asnjë policy nuk u kthen asgjë roleve anon dhe authenticated. Kjo është pika e sigurt e nisjes. Tani i ktheni secilës tabelë pikërisht aksesin që i duhet.
Hapi 2. Një policy për çdo veprim
Shkruani policy të veçanta për select, insert, update dhe delete, në vend të një të vetme për të gjitha. Lexohen më lehtë dhe e vini re më shpejt kur mungon njëra. Modeli më i zakonshëm është një përdorues që sheh dhe ndryshon vetëm rreshtat e vet.
create policy "Users read their own rows"
on public.projects for select
to authenticated
using ( (select auth.uid()) = user_id );
create policy "Users create rows for themselves"
on public.projects for insert
to authenticated
with check ( (select auth.uid()) = user_id );
create policy "Users update their own rows"
on public.projects for update
to authenticated
using ( (select auth.uid()) = user_id )
with check ( (select auth.uid()) = user_id );using vendos cilët rreshta ekzistues mund të shohë një kërkesë. with check vendos cilat vlera të reja mund të shkruajë. Një policy për update ka nevojë për të dyja, përndryshe një përdorues mund t’ia kalojë rreshtin e vet dikujt tjetër duke ndryshuar user_id. Kur auth.uid() vendoset brenda një select, Postgres e llogarit një herë për kërkesë dhe jo një herë për çdo rresht, gjë që ka rëndësi në tabela të mëdha.
Hapi 3. Ekipet dhe përmbajtja publike
Aksesi për ekipe zakonisht kalon përmes një tabele anëtarësimesh. Policy pyet nëse përdoruesi i identifikuar i përket ekipit që zotëron rreshtin.
create policy "Members read their team's rows"
on public.documents for select
to authenticated
using (
team_id in (
select team_id from public.memberships
where user_id = (select auth.uid())
)
);Edhe tabela memberships ka nevojë për RLS-në e vet, dhe një policy mbi memberships që pyet memberships futet në cikël. Zgjidhja e zakonshme është një funksion i vogël security definer që kthen ekipet e përdoruesit aktual, i përdorur nga të dyja policy-të. Përmbajtja që mund ta lexojë kushdo, si postimet e publikuara, merr një policy për rolin anon që filtron sipas një kolone që vetëm serveri mund ta vendosë.
create policy "Anyone reads published posts"
on public.posts for select
to anon, authenticated
using ( published = true );Hapi 4. Mbyllni rrugët anash saj
Shumica e rrjedhjeve që gjejmë nuk janë një policy që mungon. Janë një rrugë që i anashkalon krejt policy-të.
- Çelësi service role e anashkalon plotësisht RLS. Vendi i tij është në server dhe askund tjetër. Në një aplikacion Vite, çdo variabël që fillon me VITE_ përfundon në paketën e shfletuesit, ndaj kërkojeni çelësin në JavaScript-in e ndërtuar.
- View-t ekzekutohen me të drejtat e pronarit të tyre, zakonisht postgres, ndaj e injorojnë RLS-në e tabelave poshtë tyre. Nga Postgres 15 e tutje, krijojini me security_invoker = true.
- Funksionet security definer ekzekutohen me të drejtat e pronarit dhe mund të thirren përmes API-së si RPC. Jepuni një search_path fiks, kontrolloni auth.uid() brenda tyre dhe hiqini të drejtën execute për anon nëse një vizitor nuk duhet t’i thërrasë kurrë.
- Metadatat e përdoruesit mund t’i ndryshojë vetë përdoruesi. Një policy që i beson një roli të ruajtur në user_metadata i lejon kujtdo ta bëjë veten admin. Rolet mbajini në app_metadata ose në një tabelë ku klienti nuk shkruan dot.
- Bucket-et e Storage kanë policy-të e tyre në storage.objects. Një bucket publik ia shërben çdo skedar kujtdo që ka lidhjen.
Hapi 5. Provojeni si një sulmues
Nuk mjafton t’i lexoni policy-të. Kërkojini API-së të dhëna që nuk duhet t’i merrni. Vetëm me çelësin anon, që ndodhet tashmë në frontend-in tuaj, kërkoni drejtpërdrejt një tabelë.
curl 'https://YOUR-PROJECT.supabase.co/rest/v1/profiles?select=*' \
-H "apikey: YOUR_ANON_KEY"Nëse kthen rreshta që nuk duhet të jenë publikë, tabela është e hapur. Pastaj hyni si një përdorues dhe kërkoni sipas id-së rreshtin e një tjetri. Duhet të merrni një rezultat bosh, jo një gabim dhe jo rreshtin. Mund të veproni edhe si një përdorues në editorin SQL, brenda një transaksioni.
begin;
set local role authenticated;
select set_config('request.jwt.claims',
'{"sub":"USER-UUID","role":"authenticated"}', true);
select * from public.projects; -- only this user's rows
rollback;Security Advisor në panelin e Supabase i shënon gjithashtu tabelat e skemës public pa RLS. Është një alarm i mirë. Por nuk ju thotë nëse policy-të që keni shkruar janë të sakta.
Lista e kontrollit
- RLS është aktive në çdo tabelë të skemës public
- Çdo tabelë ka një policy të qartë për çdo veprim që lejon
- Policy-të krahasojnë me auth.uid(), kurrë me një kolonë ose metadata që klienti mund t’i vendosë
- Policy-të e update kanë edhe using edhe with check
- Çelësi service role ndodhet vetëm në kodin e serverit
- View-t përdorin security_invoker, dhe funksionet security definer kontrollojnë kush i thërret
- Bucket-et e Storage kanë policy, dhe ato publike nuk mbajnë asgjë private
- Një përdorues i identifikuar nuk mund të lexojë rreshtin e një tjetri duke ndryshuar një id
Këtë listë e bëjmë para çdo gjëje tjetër në një shpëtim, sepse një policy e vetme që mungon zakonisht do të thotë se çdo rekord në atë tabelë ka qenë i lexueshëm për këdo që ka parë. Është auditimi më i lirë në software dhe më i shtrenjti për ta anashkaluar.
