---
title: "Supabase view security definer: prečo obchádza RLS"
description: "Prečo view v Supabase vracia riadky, ktoré RLS na tabuľke skrýva, čo znamená varovanie Advisora security_definer_view a jedno nastavenie, ktoré to opraví."
url: "https://madsalt.tech/sk/notes/supabase-security-definer-view/"
author: "Denys Zaichenko"
author_url: "https://madsalt.tech/sk/about/"
language: "sk"
published: 2026-11-02
---

# Supabase view security definer: prečo obchádza RLS

TL;DR

View v Postgrese beží s právami svojho vlastníka, ak nie je vytvorené so `security_invoker = true`. V Supabase je vlastníkom zvyčajne `postgres`, ktorý RLS obchádza, takže view nad chránenou tabuľkou vracia riadky, ktoré tabuľka sama skrýva. Na mojom lokálnom stacku dostal verejný kľúč z tabuľky 0 riadkov a z obyčajného view nad ňou všetky 3 z 3; so `security_invoker = true` vrátilo view tiež 0. Na Postgrese 15 a novšom ho nastavte na každom view vo vystavenej schéme.

Môj bezplatný [bezpečnostný checker pre Supabase](https://madsalt.tech/sk/tools/supabase-security-check/) má pravidlo „Pohľady, ktoré obchádzajú RLS“ a Security Advisor od Supabase hlási to isté ako `security_definer_view`. Ľahko sa prehliadne, pretože tabuľka, z ktorej dáta unikajú, vyzerá chránene.

## Prečo view v Supabase obchádza RLS?

View je uložený dotaz. V predvolenom stave Postgres kontroluje oprávnenia a RLS na tabuľkách vo vnútri ako vlastník view, nie ako ten, kto ho volá. [Sprievodca RLS](https://supabase.com/docs/guides/database/postgres/row-level-security) od Supabase to hovorí priamo: views v predvolenom stave obchádzajú RLS, pretože sa zvyčajne vytvárajú používateľom `postgres`, a view nad chránenou tabuľkou vydá každý riadok, ktorý jej politiky mali zadržať.

Data API servíruje views rovnako ako tabuľky. Ak je view vo vystavenej schéme a `anon` alebo `authenticated` z neho smie vyberať, prečíta ho verejný kľúč.

## Čo view vrátilo v mojom teste?

Na lokálnom stacku Supabase (CLI 2.109.1, Postgres 17.6, PostgREST 14.14) som vytvoril tabuľku `orders` s tromi fiktívnymi zákazníkmi, zapol RLS bez politiky a nad ňou view `order_summary` s grantom `select` pre `anon`. Potom som oboje zavolal s publishable key:

![Rovnaká tabuľka, tri spôsoby čítania. Lokálny Supabase, RLS zapnuté bez politiky, publishable key. Samotná tabuľka: Publishable key → orders, RLS zapnuté → 0 riadkov; Obyčajné view nad ňou: Publishable key → view order_summary → 3 z 3 riadkov; View so security_invoker: Publishable key → view security_invoker → 0 riadkov.](https://madsalt.tech/charts/sk/supabase-security-definer-view.svg)

*Moja reprodukcia na lokálnom Supabase, 23. októbra 2026.*

| Požiadavka s publishable key                              | Výsledok                              |
| --------------------------------------------------------- | ------------------------------------- |
| `GET /rest/v1/orders` (RLS zapnuté, bez politiky)         | `[]`                                  |
| `GET /rest/v1/order_summary` (obyčajné view)              | všetky 3 riadky, s menami a e-mailami |
| `GET /rest/v1/order_summary` po `security_invoker = true` | `[]`                                  |

Rovnaká tabuľka, rovnaký kľúč. Odpoveď zmenilo iba nastavenie view.

## Ako to opraviť?

Na Postgrese 15 a novšom, ktorý beží na každom aktuálnom projekte Supabase, nechajte view kontrolovať RLS ako volajúci:

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

Pri novom view ho uveďte v definícii:

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

Volajúci potom potrebuje aj `select` na podkladovú tabuľku a o riadkoch rozhodujú politiky tabuľky. Ak view musí agregovať dáta, ktoré volajúci po riadkoch vidieť nesmie, napríklad verejný počet, nechajte ho mimo vystavenej schémy a ponúknite ho cez funkciu s úzkym výsledkom.

Zoznam views v `public`, ktoré stále bežia ako ich vlastník:

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

Na mojom stacku, s jedným obyčajným view a dvoma so `security_invoker = true`, vrátil iba to obyčajné.

Materializované views RLS nepodporujú vôbec, preto ich checker hlási v API osobitne, pod „Materializované pohľady alebo cudzie tabuľky v API“. Čo ďalšie obchádza politiky, od secret key po funkcie `SECURITY DEFINER`, je v mojom [checkliste bezpečnosti aplikácie z Lovable](https://madsalt.tech/sk/blog/lovable-app-security-checklist/) a ako dokázať, že samotné politiky fungujú, ukazuje [ako testujem Supabase RLS](https://madsalt.tech/sk/blog/how-to-test-supabase-rls/). Politika, ktorá vyhľadáva tímy cez pomocnú funkciu, bez rekurzie, je v článku [Supabase RLS policy: príklady otestované cez API](https://madsalt.tech/sk/blog/supabase-rls-policy-examples/). Funkcia so `SECURITY DEFINER` potrebuje ešte jedno nastavenie, pevný `search_path`; prečo v mojom teste `search_path = public` nestačilo, je v poznámke [Supabase „function search path mutable“: čo to znamená](https://madsalt.tech/sk/notes/supabase-function-search-path-mutable/).
