---
title: "Supabase security definer view: miksi se ohittaa RLS:n"
description: "Miksi Supabase-näkymä palauttaa rivejä, jotka RLS piilottaa taulusta, mitä Advisor-varoitus security_definer_view tarkoittaa ja mikä yksi asetus korjaa sen."
url: "https://madsalt.tech/fi/notes/supabase-security-definer-view/"
author: "Denys Zaichenko"
author_url: "https://madsalt.tech/fi/about/"
language: "fi"
published: 2026-11-02
---

# Supabase security definer view: miksi se ohittaa RLS:n

TL;DR

Postgres-näkymä ajetaan omistajansa oikeuksilla, ellei sitä luoda asetuksella `security_invoker = true`. Supabasessa omistaja on yleensä `postgres`, joka ohittaa RLS:n, joten suojatun taulun päälle tehty näkymä palauttaa rivejä, jotka taulu itse piilottaa. Paikallisella stackillani julkinen avain sai taulusta 0 riviä ja tavallisesta näkymästä kaikki 3 kolmesta; asetuksella `security_invoker = true` näkymäkin palautti 0. Postgres 15:ssä ja uudemmissa: aseta se jokaiselle julkaistun skeeman näkymälle.

Ilmaisessa [Supabase-tietoturvatarkistuksessani](https://madsalt.tech/fi/tools/supabase-security-check/) on sääntö nimeltä "RLS:n ohittavat näkymät", ja Supabasen Security Advisor raportoi saman asian nimellä `security_definer_view`. Se on helppo ohittaa, koska taulu, josta se vuotaa, näyttää suojatulta.

## Miksi Supabase-näkymä ohittaa RLS:n?

Näkymä on tallennettu kysely. Oletuksena Postgres tarkistaa sen sisällä olevien taulujen oikeudet ja RLS:n näkymän omistajana, ei kutsujana. Supabasen [RLS-opas](https://supabase.com/docs/guides/database/postgres/row-level-security) sanoo sen suoraan: näkymät ohittavat RLS:n oletuksena, koska ne luodaan yleensä käyttäjällä `postgres`, ja suojatun taulun päälle tehty näkymä jakaa jokaisen rivin, jonka politiikkojen piti pidättää.

Data API tarjoaa näkymät kuin taulut. Jos näkymä on julkaistussa skeemassa ja `anon` tai `authenticated` voi tehdä siihen `select`-kyselyn, julkinen avain lukee sen.

## Mitä näkymä palautti testissäni?

Paikallisella Supabase-stackilla (CLI 2.109.1, Postgres 17.6, PostgREST 14.14) loin taulun `orders` kolmella tekaistulla asiakkaalla, otin RLS:n käyttöön ilman politiikkaa ja loin sen päälle näkymän `order_summary`, jolla oli `select`-oikeus roolille `anon`. Sitten kutsuin molempia publishable keyllä:

![Sama taulu, kolme tapaa lukea se. Paikallinen Supabase, RLS päällä ilman politiikkaa, publishable key. Itse taulu: Publishable key → orders, RLS päällä → 0 riviä; Tavallinen näkymä sen päällä: Publishable key → order_summary-näkymä → 3/3 riviä; Näkymä, jolla security_invoker: Publishable key → security_invoker-näkymä → 0 riviä.](https://madsalt.tech/charts/fi/supabase-security-definer-view.svg)

*Toistoni paikallisella Supabase-stackilla, 23. lokakuuta 2026.*

| Pyyntö publishable keyllä                                                | Tulos                                       |
| ------------------------------------------------------------------------ | ------------------------------------------- |
| `GET /rest/v1/orders` (RLS päällä, ei politiikkaa)                       | `[]`                                        |
| `GET /rest/v1/order_summary` (tavallinen näkymä)                         | kaikki 3 riviä, nimineen ja sähköposteineen |
| `GET /rest/v1/order_summary` asetuksen `security_invoker = true` jälkeen | `[]`                                        |

Sama taulu, sama avain. Vain näkymän asetus muutti sen, mitä palautui.

## Miten se korjataan?

Postgres 15:ssä ja uudemmissa, joita kaikki nykyiset Supabase-projektit ajavat, anna näkymän tarkistaa RLS kutsujana:

```sql
alter view public.order_summary set (security_invoker = true);
```

Uudessa näkymässä asetus kirjoitetaan määritykseen:

```sql
create view public.order_summary
with (security_invoker = true)
as select customer, email, total from public.orders;
```

Kutsuja tarvitsee tällöin myös `select`-oikeuden alla olevaan tauluun, ja taulun politiikat ratkaisevat rivit. Jos näkymän täytyy koota dataa, jota kutsuja ei voi nähdä rivi riviltä, kuten julkinen lukumäärä, pidä se poissa julkaistusta skeemasta ja tarjoa se sen sijaan funktion kautta, jonka tulos on suppea.

Näin listaat `public`-skeeman näkymät, jotka ajetaan edelleen omistajana:

```sql
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);
```

Stackillani, jossa oli yksi tavallinen näkymä ja kaksi asetuksella `security_invoker = true`, se palautti vain tavallisen.

Materialisoidut näkymät eivät tue RLS:ää lainkaan, joten tarkistus liputtaa ne API:ssa erikseen säännöllä "Materialisoidut näkymät tai vierastaulut API:ssa". Mitä muuta kiertää politiikat, secret keystä `SECURITY DEFINER` -funktioihin, löytyy [Lovable-sovelluksen tietoturvatarkistuslistastani](https://madsalt.tech/fi/blog/lovable-app-security-checklist/), ja se, miten politiikat itse todistetaan, selviää artikkelista [Supabase RLS:n testaus](https://madsalt.tech/fi/blog/how-to-test-supabase-rls/). Politiikka, joka hakee tiimit apufunktion kautta ilman rekursiota, löytyy artikkelista [Supabase RLS policy -esimerkit testattuna API:n kautta](https://madsalt.tech/fi/blog/supabase-rls-policy-examples/). `SECURITY DEFINER` -funktio tarvitsee vielä yhden asetuksen, kiinteän `search_path`in; miksi `search_path = public` ei riittänyt testissäni, selviää muistiinpanosta [Supabase "function search path mutable": mitä se tarkoittaa](https://madsalt.tech/fi/notes/supabase-function-search-path-mutable/).
