Trumpai
Postgres view veikia su savo savininko teisėmis, jei nesukurtas su security_invoker = true. Supabase savininkas paprastai yra postgres, kuris apeina RLS, todėl view virš apsaugotos lentelės grąžina eilutes, kurias pati lentelė slepia. Mano vietinėje aplinkoje viešasis raktas iš lentelės gavo 0 eilučių, o iš paprasto view virš jos visas 3 iš 3; su security_invoker = true view taip pat grąžino 0. Postgres 15 ir naujesnėje versijoje nustatykite tai kiekvienam view pateikiamoje schemoje.
Mano nemokamas Supabase saugos tikrintuvas turi taisyklę „Vaizdai, apeinantys RLS”, o Supabase Security Advisor tą patį praneša kaip security_definer_view. Tai lengva praleisti, nes lentelė, iš kurios nutekama, atrodo apsaugota.
Kodėl Supabase view apeina RLS?
View yra išsaugota užklausa. Pagal numatytuosius nustatymus Postgres lentelių teises ir RLS joje tikrina kaip view savininko, o ne kaip jį kviečiančiojo. Supabase RLS vadovas tai sako tiesiai: view pagal numatytuosius nustatymus apeina RLS, nes paprastai kuriami su naudotoju postgres, o view virš apsaugotos lentelės išduoda kiekvieną eilutę, kurią jos politikos turėjo sulaikyti.
Data API pateikia view kaip lenteles. Jei view yra pateikiamoje schemoje ir anon ar authenticated gali iš jo atlikti select, jį skaito ir viešasis raktas.
Ką view grąžino mano teste?
Vietiniame Supabase (CLI 2.109.1, Postgres 17.6, PostgREST 14.14) sukūriau lentelę orders su trimis išgalvotais klientais, įjungiau RLS be politikos ir virš jos sukūriau view order_summary su select teise rolei anon. Tada kviečiau abu su publishable key:
| Užklausa su publishable key | Rezultatas |
|---|---|
GET /rest/v1/orders (RLS įjungtas, politikos nėra) |
[] |
GET /rest/v1/order_summary (paprastas view) |
visos 3 eilutės su vardais ir el. pašto adresais |
GET /rest/v1/order_summary po security_invoker = true |
[] |
Ta pati lentelė, tas pats raktas. Rezultatą pakeitė tik view nustatymas.
Kaip tai ištaisyti?
Postgres 15 ir naujesnėje versijoje, kurią naudoja kiekvienas dabartinis Supabase projektas, priverskite view tikrinti RLS kaip kviečiantįjį:
alter view public.order_summary set (security_invoker = true);
Naujam view įrašykite tai apibrėžime:
create view public.order_summary
with (security_invoker = true)
as select customer, email, total from public.orders;
Tada kviečiančiajam reikia ir select teisės į pagrindinę lentelę, o eilutes nusprendžia lentelės politikos. Jei view turi sumuoti duomenis, kurių kviečiantysis eilutė po eilutės nemato, pavyzdžiui, viešą skaičių, laikykite jį ne pateikiamoje schemoje ir pateikite per funkciją su siauru rezultatu.
Norėdami išvardyti public view, kurie vis dar veikia su savininko teisėmis:
select c.relname as view
from pg_class c join pg_namespace n on n.oid = c.relnamespace
where n.nspname = 'public' and c.relkind = 'v'
and not coalesce(
(select bool_or(option = 'security_invoker=true' or option = 'security_invoker=on')
from unnest(c.reloptions) as option), false);
Mano aplinkoje, kur buvo vienas paprastas view ir du su security_invoker = true, ji grąžino tik paprastąjį.
Materializuoti view RLS nepalaiko visai, todėl tikrintuvas juos API pažymi atskirai, taisykle „Medžiagintieji vaizdai ar išorinės lentelės API”. Kas dar eina pro politikas, nuo secret key iki SECURITY DEFINER funkcijų, aprašyta mano Lovable programos saugos sąraše, o kaip įrodyti pačias politikas, rasite straipsnyje kaip testuoju Supabase RLS. Politika, kuri komandų ieško per pagalbinę funkciją, be rekursijos, pateikta straipsnyje Supabase RLS policy: pavyzdžiai, išbandyti per API. Funkcijai su SECURITY DEFINER reikia dar vieno nustatymo, fiksuoto search_path; kodėl mano teste search_path = public nepakako, aprašyta pastaboje Supabase „function search path mutable“: ką tai reiškia.