Siirry sisältöön

Enable RLS in Supabase: mitä taulu palauttaa sen jälkeen

Näytä MarkdowninaAvaa palvelussa ClaudeAvaa palvelussa ChatGPTAvaa palvelussa Perplexity

Kysymys kopioidaan myös. Jos keskustelu avautuu tyhjänä, liitä se.

TL;DR

RLS otetaan käyttöön Supabasessa yhdellä lauseella taulua kohden, alter table public.todos enable row level security;, tai hallintapaneelin valinnalla “Enable RLS”. SQL-editorissa tai migraatiolla luodut taulut eivät saa sitä oletuksena. Heti käyttöönoton jälkeen taulu hiljenee eikä huuda. Paikallisessa testissäni julkinen avain ja kaksi kirjautunutta käyttäjää saivat 200 [] selectiin, updateen ja deleteen, ja vain insert epäonnistui virheellä 42501. Salainen avain näki yhä kaikki 3 riviä. Pelkkä näkymä (view) taulun päällä antoi yhä kaikki 3 riviä julkiselle avaimelle. 30. lokakuuta 2026 alkaen uusi taulu tarvitsee myös granttin ennen kuin mikään pääsee siihen, joten ota RLS käyttöön ja kirjoita politiikat samassa migraatiossa kuin grant.

Ylläpidän ilmaista Supabasen tietoturvatarkistusta, ja “RLS disabled” on löytö, jonka selitetään huonoimmin. Useimmat oppaat sanovat, että RLS:n käyttöönoton jälkeen “taulu muuttuu saavuttamattomaksi”. Se pitää paikkansa lukemisen osalta ja johtaa harhaan kirjoittamisen osalta. Siksi tein yhden taulun paikalliseen Supabase-stackiin ja lähetin siihen kaikenlaisia pyyntöjä: julkisella avaimella, kahtena kirjautuneena käyttäjänä ja salaisella avaimella, jokaisen alla olevan vaiheen jälkeen.

Mikä RLS on Supabasessa?

RLS (row-level security) on Postgresin ominaisuus, joka suodattaa rivejä pyyntökohtaisesti. Supabase muuttaa jokaisen supabase-js-pyynnön tietokantakyselyksi, joka ajetaan roolina: anon julkiselle (anon, publishable) avaimelle ja authenticated kirjautuneelle käyttäjälle. RLS päällä Postgres tarkistaa politiikkasi jokaiselle riville ja palauttaa, muuttaa tai poistaa vain ne rivit, jotka jokin politiikka sallii. RLS pois päältä mikä tahansa rooli, jolla on taulussa grant, pääsee kaikkiin riveihin.

Salainen (service role) avain on erilainen. Sen roolilla on BYPASSRLS, joten RLS ei koskaan koske siihen. Siksi se kuuluu vain palvelimelle. Ero näiden kahden avaimen välillä on artikkelissa Supabase anon key vs service role key: mitä kumpikin avaa.

Miten enable RLS in Supabase onnistuu?

Kolmella tavalla sen mukaan, miten taulu tehtiin:

  • Kun luot taulun Table Editorissa, jätä “Enable Row Level Security (RLS)” valituksi. Supabasen hallintapaneelin lähdekoodissa se on merkitty “Recommended”, ja sen poistaminen näyttää varoituksen, että taulu on julkisesti luettavissa ja kirjoitettavissa.
  • Olemassa olevalle taululle Supabasen API-turvaopas neuvoo: mene kohtaan Database, sitten Policies, ja valitse taulusta “Enable RLS”.
  • SQL:llä SQL-editorissa tai migraatiossa:
alter table public.todos enable row level security;

Supabasen opas sanoo selvästi, että hallintapaneelissa luoduissa tauluissa RLS on oletuksena päällä, mutta SQL-editorissa tai muulla työkalulla luoduissa ei ole. Jos rakennat skeeman migraatioilla tai tekoälytyökalulla, tarkista jokainen taulu.

Mitä tapahtuu heti RLS:n käyttöönoton jälkeen?

Mikään ei anna virhettä paitsi insert. Loin taulun public.todos omistajasarakkeella (user_id uuid default auth.uid()), annoin siihen granttit API-rooleille niin kuin vanhemmissa projekteissa, ja lisäsin kolme riviä: kaksi käyttäjän A ja yhden käyttäjän B. Sitten otin RLS:n käyttöön ilman politiikkaa:

Pyyntö Julkinen avain Käyttäjä A Käyttäjä B Salainen avain
Select 200, 0 riviä 200, 0 riviä 200, 0 riviä 200, 3 riviä
Insert omalla rivillä 401, virhe 42501 403, virhe 42501 403, virhe 42501 201, rivi lisätty
Update riville 200, 0 riviä 200, 0 riviä 200, 0 riviä 200, 1 rivi
Delete riville 200, 0 riviä 200, 0 riviä 200, 0 riviä 200, 1 rivi

Insert-virhe kuuluu new row violates row-level security policy. Update ja delete vastaavat 200 ja tyhjällä listalla, joten sovellus, joka ei tarkista palautettuja rivejä, ilmoittaa “tallennettu”, vaikka mitään ei tallennettu. Lue kirjoituksesi takaisin RLS:n käyttöönoton jälkeen tai pyydä lukumäärä otsakkeella Prefer: count=exact.

Ennen RLS:ää samoilla granteilla julkinen avain pystyi lukemaan kaikki 3 riviä, lisäämään rivejä sekä päivittämään tai poistamaan mitä tahansa riviä. Käyttäjä A pystyi muokkaamaan ja poistamaan käyttäjän B rivin ja antamaan omat rivinsä B:lle.

Miten taulu saadaan taas käyttökelpoiseksi?

Kirjoita politiikka jokaiselle komennolle. Tavallinen omien rivien sarja:

create policy "own select" on public.todos for select to authenticated
  using ((select auth.uid()) = user_id);
create policy "own insert" on public.todos for insert to authenticated
  with check ((select auth.uid()) = user_id);
create policy "own update" on public.todos for update to authenticated
  using ((select auth.uid()) = user_id) with check ((select auth.uid()) = user_id);
create policy "own delete" on public.todos for delete to authenticated
  using ((select auth.uid()) = user_id);

Näillä käyttäjä A näki 2 riviään ja käyttäjä B 1. Julkinen avain näki yhä 0. Kun A yritti lisätä rivin, jonka omistaja on B, tai siirtää oman rivinsä B:lle, vastaus oli 403, 42501. Kieltäytyminen tulee updaten with check -ehdosta. Kun A yritti päivittää tai poistaa B:n rivin, vastaus oli 200, 0 riviä. Lisää malleja (julkiset profiilit, tiimit, MFA) on artikkelissa Supabase RLS policy -esimerkit testattuna API:n kautta.

Yksi havainto salaisesta avaimesta: sen lisäämä rivi sai user_id = null, koska auth.uid():lla ei ole käyttäjää tämän avaimen takana. RLS ohitettiin, mutta oletusarvo ei tehnyt sitä, mitä sarakkeen nimi antaa ymmärtää.

Mitä julkinen avain luki kunkin vaiheen jälkeen?

Rivit, jotka julkinen avain luki, 3:sta. Yksi Supabase-taulu, 3 riviä, jokaisen vaiheen jälkeen. Uusi taulu, ei grantia, RLS pois (virhe 42501): 0; Grant lisätty, RLS pois: 3; RLS päällä, ei politiikkaa: 0; RLS päällä, omien rivien politiikat: 0; Pelkkä näkymä taulun päällä: 3; Näkymä, security_invoker: 0.
Kuva 1. Paikallinen Supabase-stack (CLI 2.109.1, Postgres 17.6), 28. lokakuuta 2026.

Kaksi vaihetta vuotaa kaiken: grant ilman RLS:ää ja pelkkä näkymä taulun päällä, jonka politiikat ovat kunnossa. Seuraavat kaksi osiota selittävät molemmat.

Tarvitaanko RLS:ää yhä 30. lokakuun grant-muutoksen jälkeen?

Kyllä. 30. lokakuuta 2026 alkaen Supabase ei enää anna uusille tauluille skeemassa public automaattisia granteja API-rooleille, uusissa ja olemassa olevissa projekteissa. Paikallinen stackini toimii jo niin. Juuri luotu taulu, jolla RLS oli pois päältä, vastasi 42501 kaikille rooleille, salainen avain mukaan lukien, joten mikään ei vuotanut. Yksityiskohdat ovat artikkelissa Supabasen grant-muutos 30. lokakuuta: mikä hajoaa.

Mutta grant palaa heti, kun sovellus tarvitsee taulun, ja siitä lähtien RLS on ainoa asia julkisen avaimen ja jokaisen rivin välissä. Laita siis kaikki yhteen migraatioon tässä järjestyksessä:

create table public.todos (...);
alter table public.todos enable row level security;
create policy "own select" on public.todos ...;
grant select, insert, update, delete on table public.todos to authenticated;

Anna grant vain rooleille, jotka taulua tarvitsevat. Kun peruutin kaiken roolilta anon, julkisen avaimen select muuttui vastauksesta 200 [] vastaukseksi 401, 42501. Grant on ensimmäinen lukko, RLS toinen.

Miksi näkymä silti vuotaa rivejä?

Koska näkymä ajetaan omistajansa oikeuksilla, ja Supabasessa se on postgres, joka ohittaa RLS:n. Kun todos-taulussa oli omien rivien politiikat, pelkkä create view public.todos_view as select * from public.todos palautti kaikki 3 riviä julkiselle avaimelle ja käyttäjälle B.

Kun sama näkymä luotiin uudelleen with (security_invoker = true), se palautti julkiselle avaimelle 0 riviä, A:lle 2 ja B:lle 1, täsmälleen kuten taulu:

create view public.todos_view with (security_invoker = true) as
  select * from public.todos;

Supabasen Advisor liputtaa pelkän näkymän tunnuksella security_definer_view; mitä se tarkoittaa ja miten sen korjaa, on muistiinpanossa Supabase security definer view: miksi se ohittaa RLS:n.

Miten RLS otetaan käyttöön automaattisesti jokaiselle uudelle taululle?

Tapahtumatriggerillä. Supabasen event triggers -opas antaa funktion rls_auto_enable() ja triggerin ensure_rls, joka ajetaan create table-, create table as- ja select into -lauseiden jälkeen skeemassa public. Asensin sen sellaisenaan julkaistuna ja sain:

  • Myöhemmin lauseella create table luodussa taulussa RLS oli päällä ilman alter-lausetta. Julkinen avain sai 200 [] selectiin ja 401 insertiin.
  • Lauseella create table as select luodussa taulussa RLS oli myös päällä.
  • Ennen triggeriä luotu taulu pysyi pois päältä. Trigger ei toimi taaksepäin.

Ilman triggeriä suojatun taulun viereen luodussa toisessa taulussa RLS oli pois päältä, ja kun siihen annettiin grant, julkinen avain luki ja kirjoitti sitä vapaasti. Trigger kattaa myös vain skeeman public; muut julkaistut skeemat tarvitsevat oman rivinsä funktioon.

Auttaako FORCE ROW LEVEL SECURITY?

Ei niille rooleille, joista kannattaa olla huolissaan. alter table ... force row level security panee RLS:n koskemaan myös taulun omistajaa. Supabasessa sekä postgres että service_role ovat BYPASSRLS, joten forcen jälkeen omistaja laski yhä 3 riviä, salainen avain näki yhä kaiken ja postgresin omistama näkymä vuoti yhä. Se muutti jotain vain omistajaroolille, jonka loin ilman BYPASSRLS: 2 riviä ennen, 0 jälkeen.

Miten tarkistat oman projektisi?

  • Listaa SQL-editorissa skeeman public taulut, joilla RLS on pois päältä:
select c.relname as table_name
from pg_class c
join pg_namespace n on n.oid = c.relnamespace
where n.nspname = 'public'
  and c.relkind in ('r', 'p')
  and not c.relrowsecurity
order by 1;

Miten testasin

  • Supabase CLI 2.109.1 kannettavalla: Postgres 17.6, PostgREST 14.14, Auth 2.192.0. Kaksi käyttäjää kirjautui salasanalla, lisäksi paikalliset publishable- ja secret-avaimet.
  • Yksi taulu, public.todos, alustettu samoilla 3 rivillä ennen jokaista pyyntöä. Jokainen vaihe ajettiin SQL:nä ja jokainen pyyntö kulki REST API:n kautta, tilakoodi ja runko tallennettuna.
  • Vaiheiden 2–5 granttit lisättiin käsin vastaamaan projektia, joka on luotu ennen 30. lokakuuta 2026.

Rajoitukset. Yksi paikallinen stack, keksittyä dataa, 28. lokakuuta 2026. Hallintapaneelin vaiheet on lainattu Supabasen dokumentaatiosta, ei klikattu läpi. Hallintapaneelin oman roolin luomilla tauluilla voi olla toiset oletusgranttit. En testannut sitä enkä Storagea, Realtimea tai GraphQL:ää.

Etkö ole varma, ovatko taulusi lukittu?

Katson mielelläni skeemaa kanssasi. Ei myyntipuhetta.

Ota yhteyttä

© 2026 Denys Zaichenko. Alkuperäinen artikkeli: https://madsalt.tech/fi/blog/how-to-enable-rls-in-supabase/