Nejhorší noční můra každé aplikace je, když uživatel A uvidí data uživatele B. Faktury, zprávy, osobní údaje. Row Level Security (RLS) je mechanismus, který tomuhle zabrání přímo na úrovni databáze, takže se na něj můžeš spolehnout, i když uděláš chybu jinde v kódu. Vysvětlím ti ho bez žargonu a ukážu, jak ho zapnout krok za krokem.
Co Row Level Security vlastně dělá
Normálně platí, že kdo se dostane k databázi, vidí v tabulce všechny řádky. RLS to obrátí: ke každé tabulce přidáš pravidlo, které říká, kdo smí který řádek vidět a měnit. Databáze pak u každého dotazu sama odfiltruje řádky, na které uživatel nemá nárok, ještě než ti je vrátí.
Klíčové slovo je sama. Nemusíš si pamatovat, že do každého dotazu přidáš podmínku jen moje data. Databáze to vynutí za tebe pokaždé. To je obrovský rozdíl v bezpečnosti, protože jediná zapomenutá podmínka v kódu jinak znamená únik dat, a takových podmínek máš v aplikaci stovky.
Proč nestačí kontrolovat to v aplikaci
Spousta lidí řeší přístup jen v kódu aplikace. Funguje to, dokud někde neuděláš chybu, nepřidáš nový endpoint bez kontroly, nebo se ti někdo nedostane k databázovému připojení napřímo. RLS je poslední záchranná síť, která drží, i když všechno ostatní selže. Je to rozdíl mezi jednou dveřmi a dveřmi i mříží.
Beru to jako vrstvenou obranu: kontrola v aplikaci plus RLS v databázi. Když selže jedna vrstva, druhá pořád chrání. Víc o tomhle přístupu píšu v článku o bezpečnosti aplikace pro neprogramátory, kde rozebírám i další běžné díry.
Jak RLS vypadá v praxi
V Postgresu a Supabase se RLS zapíná na konkrétní tabulce a pak k ní přidáš jednu nebo víc politik. Ukážu ti to na tabulce faktur, kde má každý uživatel vidět jen ty svoje, nikoho jiného.
-- 1) zapni RLS na tabulce
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY;
-- 2) politika: uživatel vidí jen své faktury
CREATE POLICY "vlastni faktury jen ke cteni"
ON invoices
FOR SELECT
USING (user_id = auth.uid());Ta podmínka v USING je jádro celé věci. Říká pusť řádek jen tehdy, když jeho user_id odpovídá ID přihlášeného uživatele. Funkce auth.uid() v Supabase vrací ID právě přihlášeného člověka, takže se podmínka vyhodnotí pro každého jinak.
USING versus WITH CHECK
U politik narazíš na dvě podobně znějící klauzule, které ale dělají různé věci. Tady je rozdíl, ať je nepleteš, protože záměna je častý zdroj děr.
| Klauzule | Platí pro | Co kontroluje |
|---|---|---|
| USING | SELECT, UPDATE, DELETE | Které řádky uživatel vidí a smí na ně sáhnout |
| WITH CHECK | INSERT, UPDATE | Jestli nově zapisovaný řádek splňuje pravidlo |
-- uživatel smí vkládat faktury jen sám pod sebe
CREATE POLICY "vkladani jen vlastnich"
ON invoices
FOR INSERT
WITH CHECK (user_id = auth.uid());Bez WITH CHECK by si chytrý uživatel mohl vytvořit fakturu s cizím user_id. USING by mu pak sice zabránil ji vidět, ale data už by byla podstrčená do cizího účtu. Proto u zápisů potřebuješ obojí, jinak ti obrana zůstane jen poloviční.
Praktická pravidla, která se vyplatí
Pár věcí, které jsem se naučil za roky práce s RLS a které ti ušetří problémy a zbytečné nervy při ladění.
- Zapni RLS hned při založení tabulky. Dolepovat ho na produkci s daty je nervózní.
- Default je zákaz. Když máš RLS zapnuté a žádnou politiku, nevidíš nic. To je správně, je to bezpečné výchozí nastavení.
- Piš zvlášť politiku pro každou operaci. SELECT, INSERT, UPDATE a DELETE mohou mít různá pravidla.
- Testuj jako reálný uživatel, ne jako admin. Admin často RLS obchází, takže s ním nic neověříš.
Pozor na servisní klíče
V Supabase existuje servisní klíč, který RLS úplně obchází. Je určený pro serverové operace, kde máš plnou kontrolu a víš, co děláš. Nikdy ho nedávej do frontendu ani do prohlížeče. Kdo ho získá, vidí úplně všechno bez ohledu na politiky a tvá izolace je k ničemu. To je nejčastější díra, kterou u začátečníků vidím, a bohužel i jedna z nejnebezpečnějších.
Jak RLS otestovat, ať máš jistotu
Politika, kterou jsi nikdy nezkusil obejít, je jen zbožné přání. Než se na RLS spolehneš, založ si dva testovací účty a vyzkoušej, jestli jeden opravdu nevidí data druhého. Zkus to i přes API, ne jen v administraci databáze, protože ta často běží pod servisním klíčem a RLS obchází.
Doporučuju si na to napsat jednoduchý test, který přihlásí uživatele A a pokusí se přečíst řádek uživatele B. Když test selže a data se neobjeví, je to dobře, izolace funguje. Tohle pár minut práce ti dá klid, že se ti citlivá data nevylijou ven, a budeš to umět kdykoli rychle ověřit po každé změně.
Časté dotazy
Zpomalí RLS databázi?
Trochu ano, protože databáze u každého dotazu vyhodnocuje politiku. V praxi je to zanedbatelné, pokud máš index na sloupci, podle kterého filtruješ (typicky user_id nebo tenant_id). Bez indexu se to při velkém objemu dat projevit může.
Funguje RLS jen v Supabase?
Ne, je to standardní funkce PostgreSQL. Supabase ji jen pohodlně zpřístupňuje a propojuje s přihlášením uživatelů. Stejný princip použiješ na jakémkoli Postgresu, jen si funkci pro získání ID uživatele napíšeš po svém.
Musím mít RLS, když kontroluju přístup v kódu?
Nemusíš, ale rozhodně bys měl. Kontrola v kódu a RLS se nevylučují, doplňují se. Dvě vrstvy obrany znamenají, že jedna chyba neznamená rovnou únik dat všech uživatelů.
Správně nastavená izolace dat je něco, co se nevyplatí podcenit, zvlášť pokud zpracováváš osobní nebo platební údaje a vztahuje se na tebe GDPR. Pokud chceš mít jistotu, že je tvá aplikace zabezpečená pořádně, ozvi se mi přes kontakt a podívám se na ni.