Īsumā
Postgres skats darbojas ar sava īpašnieka tiesībām, ja vien tas nav izveidots ar security_invoker = true. Supabase īpašnieks parasti ir postgres, kas apiet RLS, tāpēc skats virs aizsargātas tabulas atgriež rindas, ko pati tabula slēpj. Manā lokālajā vidē publiskā atslēga no tabulas ieguva 0 rindu, bet no parasta skata virs tās visas 3 no 3; ar security_invoker = true skats arī atgrieza 0. Postgres 15 un jaunākā versijā iestatiet to katram skatam atklātā shēmā.
Manai bezmaksas Supabase drošības pārbaudei ir noteikums “Skati, kas apiet RLS”, un Supabase Security Advisor to pašu ziņo kā security_definer_view. To viegli nepamanīt, jo tabula, no kuras tas noplūst, izskatās aizsargāta.
Kāpēc Supabase skats apiet RLS?
Skats ir saglabāts vaicājums. Pēc noklusējuma Postgres tabulu tiesības un RLS tajā pārbauda kā skata īpašniekam, nevis kā tam, kas to izsauc. Supabase RLS ceļvedis to saka tieši: skati pēc noklusējuma apiet RLS, jo parasti tiek veidoti ar lietotāju postgres, un skats virs aizsargātas tabulas izsniedz katru rindu, ko tās politikām bija jāatturē.
Data API sniedz skatus kā tabulas. Ja skats ir atklātā shēmā un anon vai authenticated var no tā veikt select, to nolasa arī publiskā atslēga.
Ko skats atgrieza manā testā?
Lokālā Supabase vidē (CLI 2.109.1, Postgres 17.6, PostgREST 14.14) izveidoju tabulu orders ar trim izdomātiem klientiem, ieslēdzu RLS bez politikas un virs tās izveidoju skatu order_summary ar select tiesībām lomai anon. Tad izsaucu abus ar publishable key:
| Pieprasījums ar publishable key | Rezultāts |
|---|---|
GET /rest/v1/orders (RLS ieslēgts, politikas nav) |
[] |
GET /rest/v1/order_summary (parasts skats) |
visas 3 rindas ar vārdiem un e-pastiem |
GET /rest/v1/order_summary pēc security_invoker = true |
[] |
Tā pati tabula, tā pati atslēga. Atgriezto mainīja tikai skata iestatījums.
Kā to labot?
Postgres 15 un jaunākā versijā, ko darbina katrs pašreizējais Supabase projekts, liekiet skatam pārbaudīt RLS kā izsaucējam:
alter view public.order_summary set (security_invoker = true);
Jaunam skatam ierakstiet to definīcijā:
create view public.order_summary
with (security_invoker = true)
as select customer, email, total from public.orders;
Tad izsaucējam vajag arī select uz pamattabulu, un rindas nosaka tabulas politikas. Ja skatam jāapkopo dati, ko izsaucējs pa rindām neredz, piemēram, publisks skaits, turiet to ārpus atklātās shēmas un sniedziet caur funkciju ar šauru rezultātu.
Lai izvilktu public skatus, kas joprojām darbojas ar īpašnieka tiesībām:
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);
Manā vidē, kur bija viens parasts skats un divi ar security_invoker = true, tas atgrieza tikai parasto.
Materializētie skati RLS neatbalsta vispār, tāpēc pārbaude tos API atzīmē atsevišķi, noteikumā “Materializētie skati vai ārējās tabulas API”. Kas vēl iet gar politikām, no secret key līdz SECURITY DEFINER funkcijām, ir manā Lovable lietotnes drošības kontrolsarakstā, bet kā pierādīt pašas politikas, ir rakstā kā es testēju Supabase RLS. Politika, kas meklē komandas caur palīgfunkciju bez rekursijas, ir rakstā Supabase RLS policy piemēri, pārbaudīti caur API. Funkcijai ar SECURITY DEFINER vajadzīgs vēl viens iestatījums, fiksēts search_path; kāpēc search_path = public manā testā nepietika, ir piezīmē Supabase “function search path mutable”: ko tas nozīmē.