Pereiti prie turinio

Supabase „function search path mutable“: ką tai reiškia

Supabase

Žiūrėti kaip MarkdownAtidaryti per ClaudeAtidaryti per ChatGPTAtidaryti per Perplexity

Klausimas taip pat nukopijuojamas. Jei pokalbis atsidaro tuščias, įklijuokite jį.

TL;DR

Įspėjimas reiškia, kad funkcija neturi fiksuoto search_path, todėl kiekvienas nekvalifikuotas vardas joje, pavyzdžiui, admins, ieškomas taip, kaip nurodo iškvietėjo sesija. Mano vietiniame teste funkcija is_admin() su SECURITY DEFINER vartotojui, kuris nėra adminas, grąžino true, kai jis sukūrė laikinąją lentelę admins, ir fiksuotas search_path = public to nesustabdė. Padėjo set search_path = '' su kiekviena lentele, užrašyta kaip public.admins.

Supabase Security Advisor tai nurodo kaip lint 0011, „Function Search Path Mutable“, lygis WARN, su detale „Function public.x has a role mutable search_path“. Jis pažymi kiekvieną funkciją be šio nustatymo, įskaitant SECURITY INVOKER, todėl projekte su daug pagalbinių funkcijų sąrašas ilgas: vienas atvirojo kodo projektas ištaisė 99 iš jų viena migracija.

Ką reiškia „role mutable search_path“?

search_path yra schemų sąrašas, kuriame Postgres ieško vardo be schemos priekyje. Funkcija be savo nustatymo naudoja iškvietėjo sąrašą. Supabase lint puslapis rekomenduoja jį nustatyti į tuščią eilutę, o tai priverčia kiekvieną nuorodą funkcijos viduje rašyti pilnai kvalifikuotą.

Funkcijai su SECURITY DEFINER tai svarbiausia: ji veikia su savo savininko teisėmis, paprastai postgres, kuris apeina RLS. Jei iškvietėjas gali valdyti, kurią lentelę admins funkcija skaito, jis valdo patikrą, veikiančią kaip postgres.

Ar mano teste tuo galima pasinaudoti?

Vietiniame Postgres 17.10 sukūriau lentelę public.admins su vienu adminu ir keturias tos pačios funkcijos versijas, select exists (select 1 from admins where user_id = uid), visas su SECURITY DEFINER. Tada prisijungiau kaip mažų teisių rolė, kuri gali vykdyti SQL ir pagal nutylėjimą gali kurti laikinąsias lenteles, ir sukūriau pg_temp.admins su vartotoju A, kuris nėra adminas:

is_admin(A) po laikinosios lentelės admins. SECURITY DEFINER funkcija, vietinis Postgres 17.10. Be search_path: pg_temp.admins → admins → temp → true; search_path = public: pg_temp.admins → admins → temp → true; search_path = '' + public.admins: pg_temp.admins → public.admins → false.
Mano atkūrimas vietiniame Postgres, 2026 m. spalio 24 d.
Funkcijos nustatymas Lint sąlyga is_admin(A) po laikinosios lentelės
nėra pažymėta true
set search_path = public praeina true
set search_path = '', kūnas naudoja public.admins praeina false
set search_path = public, pg_temp praeina false

Antroji eilutė mane nustebino. Supabase prižiūrėtojas 2024 m. parašė, kad fiksuotas netuščias kelias nekelia jokios saugumo rizikos, ir lint jį priima. Tačiau Postgres pirmiausia ieško laikinojoje schemoje, nebent kelyje nurodytas pg_temp, kaip įspėja jo dokumentacija apie SECURITY DEFINER funkcijų rašymą. Taigi search_path = public vis tiek leidžia laikinajai lentelei užgožti nekvalifikuotą vardą.

Kas tai iš tikrųjų gali padaryti Supabase?

Ne lankytojas per jūsų programą. Data API negali kurti laikinųjų lentelių ar keisti sesijos search_path, todėl vien publishable key iki to nepasiekia. Reikia tiesioginio prisijungimo prie duomenų bazės: antros paslaugos su savo prisijungimu, BI ar ataskaitų įrankio, rangovo tik skaitymo rolės, nutekėjusio connection string. Daugumoje mažų programų rizika maža; bet kuriame projekte, kur duomenų bazės prisijungimus turi kiti žmonės ar įrankiai, o ne jūs, pirmiausia ištaisykite definer funkcijas.

Kaip tai ištaisyti?

Sukurkite funkciją iš naujo su tuščiu keliu ir schemų vardais kūne:

create or replace function public.is_admin(uid uuid)
returns boolean
language sql stable security definer
set search_path = ''
as $$ select exists (select 1 from public.admins where user_id = uid) $$;

revoke execute on function public.is_admin(uuid) from public, anon;

revoke svarbus ne mažiau nei kelias: definer funkciją public schemoje galima iškviesti per Data API, todėl be jo bet kuris svečias gali per /rpc/is_admin paklausti, ar duotas vartotojas yra adminas. Grąžinkite jį tik rolėms, kurioms jo reikia, arba laikykite tokias pagalbines funkcijas schemoje, kurios API neatveria.

Nevykdykite vien alter function public.is_admin(uuid) set search_path = ''. Tai nutildo Advisor ir sugadina funkciją: mano paleidime kitas iškvietimas nepavyko su 42P01: relation "admins" does not exist. Plėtinių operatoriams taip pat reikia jų schemos, pavyzdžiui, operator(extensions.<=>) pgvector.

Sąrašas to, ką lint pažymės public, su definer funkcijomis pirmiausia:

select p.proname, p.prosecdef as security_definer
from pg_proc p join pg_namespace n on n.oid = p.pronamespace
where n.nspname = 'public'
  and not exists (select 1 from unnest(coalesce(p.proconfig, '{}')) c
                  where c like 'search_path=%')
order by p.prosecdef desc, p.proname;

Ar pataisymas ką nors kainuoja?

Kartais. Bet kuri set sąlyga neleidžia Postgres įterpti paprastos SQL funkcijos į ją kviečiančią užklausą, kaip nurodyta GitHub. Išmatavau abi formas su 200 000 eilučių ir indeksu filtruojamame stulpelyje (7 paleidimų mediana):

SQL funkcija, paskui filtruojama pagal savininką Be set set search_path = ''
Filtras išorėje: visible_items() where owner = ... 0,053 ms, indeksas naudotas 36,7 ms, full scan
Filtras viduje: items_of(owner) 0,056 ms 0,082 ms

Jei aibę grąžinanti SQL funkcija užklausiama su filtru už jos ribų, prieš fiksuodami kelią perkelkite filtrą į vidų kaip parametrą.

Patikrinkite savo projektą

© 2026 Denys Zaichenko. Originalus straipsnis: https://madsalt.tech/lt/notes/supabase-function-search-path-mutable/