---
title: "Supabase Security-Definer-View: warum sie RLS umgeht"
description: "Warum eine Supabase-View Zeilen zeigt, die RLS verbirgt, was die Advisor-Warnung security_definer_view heißt und welche Einstellung das behebt."
url: "https://madsalt.tech/de/notes/supabase-security-definer-view/"
author: "Denys Zaichenko"
author_url: "https://madsalt.tech/de/about/"
language: "de"
published: 2026-11-02
---

# Supabase Security-Definer-View: warum sie RLS umgeht

Kurz gesagt

Eine Postgres-View läuft mit den Rechten ihres Eigentümers, es sei denn, sie wird mit `security_invoker = true` angelegt. In Supabase ist der Eigentümer meist `postgres`, und der umgeht RLS: Eine View über einer geschützten Tabelle liefert also Zeilen, die die Tabelle selbst verbirgt. Auf meinem lokalen Stack bekam der öffentliche Key 0 Zeilen aus der Tabelle und alle 3 von 3 aus einer einfachen View darüber; mit `security_invoker = true` lieferte die View ebenfalls 0. Ab Postgres 15 setzen Sie es auf jeder View in einem exponierten Schema.

Mein kostenloser [Supabase-Sicherheitscheck](https://madsalt.tech/de/tools/supabase-security-check/) hat eine Regel namens "Views, die RLS umgehen", und der Security Advisor von Supabase meldet dasselbe als `security_definer_view`. Man übersieht es leicht, denn die Tabelle, aus der es leckt, sieht geschützt aus.

## Warum umgeht eine Supabase-View RLS?

Eine View ist eine gespeicherte Abfrage. Standardmäßig prüft Postgres Rechte und RLS der Tabellen darin als Eigentümer der View, nicht als die Person, die sie aufruft. Der [RLS-Leitfaden](https://supabase.com/docs/guides/database/postgres/row-level-security) von Supabase sagt es direkt: Views umgehen RLS standardmäßig, weil sie meist mit dem Nutzer `postgres` angelegt werden, und eine View über einer geschützten Tabelle gibt jede Zeile heraus, die ihre Policies zurückhalten sollten.

Die Data API liefert Views wie Tabellen aus. Liegt die View in einem exponierten Schema und dürfen `anon` oder `authenticated` sie lesen, liest sie der öffentliche Key.

## Was lieferte die View in meinem Test?

Auf einem lokalen Supabase-Stack (CLI 2.109.1, Postgres 17.6, PostgREST 14.14) legte ich eine Tabelle `orders` mit drei erfundenen Kunden an, aktivierte RLS ohne Policy und legte darüber eine View `order_summary` mit einem `select`-Grant für `anon` an. Dann rief ich beide mit dem publishable key ab:

![Dieselbe Tabelle, drei Wege sie zu lesen. Lokales Supabase, RLS an ohne Policy, publishable key. Die Tabelle selbst: Publishable key → orders, RLS an → 0 Zeilen; Eine einfache View darüber: Publishable key → View order_summary → 3 von 3 Zeilen; Die View mit security_invoker: Publishable key → View security_invoker → 0 Zeilen.](https://madsalt.tech/charts/de/supabase-security-definer-view.svg)

*Meine Nachstellung auf einem lokalen Supabase-Stack, 23. Oktober 2026.*

| Anfrage mit dem publishable key                             | Ergebnis                             |
| ----------------------------------------------------------- | ------------------------------------ |
| `GET /rest/v1/orders` (RLS an, keine Policy)                | `[]`                                 |
| `GET /rest/v1/order_summary` (einfache View)                | alle 3 Zeilen, mit Namen und E-Mails |
| `GET /rest/v1/order_summary` nach `security_invoker = true` | `[]`                                 |

Dieselbe Tabelle, derselbe Key. Nur die Einstellung der View änderte, was zurückkam.

## Wie beheben Sie das?

Ab Postgres 15, das jedes aktuelle Supabase-Projekt nutzt, lassen Sie die View RLS als Aufrufer prüfen:

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

Für eine neue View gehört es in die Definition:

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

Der Aufrufer braucht dann auch `select` auf der zugrunde liegenden Tabelle, und deren Policies entscheiden über die Zeilen. Muss eine View Daten aggregieren, die der Aufrufer nicht zeilenweise sehen darf, etwa eine öffentliche Zahl, halten Sie sie aus dem exponierten Schema heraus und liefern sie stattdessen über eine Funktion mit schmalem Ergebnis aus.

So listen Sie die Views in `public` auf, die noch als ihr Eigentümer laufen:

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

Auf meinem Stack, mit einer einfachen View und zwei mit `security_invoker = true`, lieferte sie nur die einfache zurück.

Materialized Views unterstützen RLS gar nicht, deshalb meldet der Check sie getrennt unter "Materialized Views oder Foreign Tables in der API". Was sonst noch an Policies vorbeigeht, vom secret key bis zu `SECURITY DEFINER`-Funktionen, steht in meiner [Sicherheits-Checkliste für Lovable-Apps](https://madsalt.tech/de/blog/lovable-app-security-checklist/), und wie Sie die Policies selbst beweisen, in [Supabase RLS testen](https://madsalt.tech/de/blog/how-to-test-supabase-rls/). Eine Policy, die Teams über eine Hilfsfunktion ohne Rekursion nachschlägt, steht in [Supabase RLS Policy Beispiele, je per API getestet](https://madsalt.tech/de/blog/supabase-rls-policy-examples/). Eine Security-Definer-Funktion braucht noch eine Einstellung, einen festen `search_path`; warum `search_path = public` in meinem Test nicht reichte, steht in [Supabase Function Search Path Mutable: was die Warnung heißt](https://madsalt.tech/de/notes/supabase-function-search-path-mutable/).
