// docs / baas security / supabase rls scanner
Scaner RLS Supabase: găsește tabele cu securitate la nivel de rând lipsă sau defectă
Securitatea la nivel de rând (RLS) este singurul lucru care stă între datele clienților tăi și internet atunci când lansezi o aplicație bazată pe Supabase. Instrumentele de codare AI generează cod în formă de RLS care se compilează, se livrează și pierde date în tăcere — tabele create fără RLS activat, politici care citesc dar nu restricționează niciodată, predicate care compară o coloană cu ea însăși. Acest articol arată ce poate dovedi un scaner RLS Supabase din exterior, cele patru forme de RLS defect care apar în aplicațiile vibe-coded și cum să-ți scanezi propriul deployment în mai puțin de un minut.
Ce poate dovedi o scanare RLS externă
O scanare RLS pasivă rulează împotriva endpoint-ului PostgREST pe care Supabase îl expune la https://[project].supabase.co/rest/v1/. Folosește doar cheia anon publicabilă — aceeași cheie pe care o folosește browser-ul tău — și sondează metadatele listei de tabele, citirile anonime și scrierile anonime. Nu se autentifică niciodată ca utilizator și nu atinge niciodată privilegii de service-role. Orice poate face ea, un atacator neautentificat pe internet poate face.
Din afara bazei de date, un scaner poate confirma următoarele cu încredere ridicată:
- RLS este dezactivat pe o tabelă. PostgREST returnează rânduri pentru un
SELECTanonim atunci când RLS este dezactivat sau când o politică o permite. Ambele cazuri sunt un rezultat. - Rolul anonim poate lista tabelele. Un
GET /rest/v1/cu cheia anon returnează schema OpenAPI pentru fiecare tabelă pe care rolulanonare vreun privilegiu. Aplicațiile generate de AI acordă frecventUSAGEpe schemă șiSELECTpe fiecare tabelă, ceea ce expune harta completă a schemei chiar și când RLS neagă citirile efective. - Rolul anonim poate insera. Un
POSTde sondare cu o ghicire despre forma coloanelor va reuși dacă RLS nu are o politicăINSERTcare să-l nege — chiar dacăSELECTeste blocat. - Cheia service-role este în bundle-ul browser-ului. Adiacent cu RLS: dacă un scaner găsește
SUPABASE_SERVICE_ROLE_KEYsau orice JWT curole: service_roleîn bundle-ul JavaScript, RLS este irelevant — deținătorul acelei chei ocolește orice politică.
Ce nu poate dovedi o scanare externă
Fii sincer cu privire la limitele scanerului. O scanare RLS externă nu poate citi tabela ta pg_policies, fișierele tale de migrare sau predicatul exact al vreunei politici. Deduce din comportamentul black-box, ceea ce înseamnă că uneori va raporta un rezultat care se dovedește a fi date publice intenționate (o tabelă newsletter de marketing, un catalog public de produse). Raportul FixVibe le marchează cu încredere medie când scanerul nu poate dezambigua intenția — verifică numele tabelei și decide.
Cele patru forme de RLS defect pe care instrumentele AI le produc
Când îndrepți Cursor, Claude Code, Lovable sau Bolt către Supabase, apar aceleași patru tipare de RLS defect în mii de aplicații. Fiecare trece de verificarea de tip, compilează și se livrează:
Forma 1: RLS niciodată activat
Cel mai comun mod de eșec. Migrarea creează tabela, dar dezvoltatorul (sau instrumentul AI) uită ALTER TABLE ... ENABLE ROW LEVEL SECURITY. PostgREST servește bucuros întreaga tabelă oricui are cheia anon. Remediere: ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY; ALTER TABLE public.[name] FORCE ROW LEVEL SECURITY;. FORCE nu este opțional — fără el, proprietarul tabelei (și orice rol cu proprietatea tabelei) ocolește RLS.
ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.[name] FORCE ROW LEVEL SECURITY;Forma 2: RLS activat, fără politici
Un eșec mai subtil. RLS este activat, dar nu se scriu politici. Implicit în PostgreSQL este refuză, așa că utilizatorii autentificați nu văd nimic — iar dezvoltatorul adaugă USING (true) pentru a face aplicația să funcționeze, ceea ce permite tuturor să citească totul. Remediere: scrie o politică care să limiteze prin auth.uid(): CREATE POLICY "select_own" ON public.[name] FOR SELECT USING (auth.uid() = user_id); și o politică INSERT/UPDATE/DELETE corespunzătoare.
CREATE POLICY "select_own"
ON public.[name]
FOR SELECT
USING (auth.uid() = user_id);Forma 3: politica compară coloana cu ea însăși
A copy-paste artefact. The developer writes <code>USING (user_id = user_id)</code> — which is always true — instead of <code>USING (auth.uid() = user_id)</code>. Type-checks pass; the policy permits every row. <strong>Fix:</strong> always compare a column to a function call (<code>auth.uid()</code>, <code>auth.jwt()->>'org_id'</code>, etc.), never to itself or to a constant.
Forma 4: politică pe SELECT dar nu pe INSERT/UPDATE
Dezvoltatorul blochează citirile, dar uită scrierile. Politicile RLS sunt pe-comandă. FOR SELECT protejează doar citirile; un client anonim poate încă să facă INSERT dacă nicio politică nu îl neagă. Remediere: autorizează o politică pe comandă sau folosește FOR ALL cu clauze USING și WITH CHECK explicite.
Cum funcționează scanerul RLS Supabase de la FixVibe
Verificarea baas.supabase-rls rulează în trei etape, fiecare cu niveluri explicite de încredere:
- Etapa 1 — amprentare. Scanerul parcurge aplicația deployată, parsează bundle-ul JavaScript și extrage URL-ul proiectului Supabase și cheia anon din configurația de runtime. Nicio ghicire DNS, nicio brute force — citește ceea ce citește browser-ul.
- Etapa 2 — descoperirea schemei. Un singur
GET /rest/v1/cu cheia anon returnează schema OpenAPI pentru fiecare tabelă pe care rolul anon o poate vedea. Scanerul înregistrează numele tabelelor, dar nu citește date de rând în această etapă. - Etapa 3 — sonde de citire și scriere. Pentru fiecare tabelă descoperită, scanerul emite un
SELECTanonim culimit=1. Dacă se returnează rânduri, RLS este permisiv. Scanerul se oprește acolo — nu enumeră rânduri, nu paginează, nu modifică date. Sondele INSERT sunt condiționate de proprietatea domeniului verificată și opt-in explicit; nu se declanșează niciodată împotriva țintelor neverificate.
Fiecare rezultat este livrat cu URL-ul exact al cererii, statusul răspunsului, forma răspunsului (doar antetul) și numele tabelei. Prompt-ul de remediere AI din partea de jos a rezultatului este un bloc SQL pe care îl rulezi în editorul SQL Supabase.
Ce să faci când scanerul găsește ceva
Fiecare rezultat RLS este o urgență în producție. Endpoint-urile PostgREST publice sunt scanate de atacatori în câteva minute. Secvența de remediere este mecanică:
- Auditează fiecare tabelă. Rulează
SELECT schemaname, tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public';în editorul SQL Supabase. Orice rând curowsecurity = falseeste o problemă. - Activează RLS pe fiecare tabelă publică. Setează implicit
ENABLE ROW LEVEL SECURITYșiFORCE ROW LEVEL SECURITYpe fiecare tabelă creată — fă din asta un șablon de migrare. - Autorizează politici comandă-cu-comandă. Nu folosi
FOR ALL USING (true). Scrie politici explicite pentru SELECT, INSERT, UPDATE, DELETE — fiecare delimitată laauth.uid()sau la o coloană org-id dinauth.jwt(). - Verifică cu un al doilea cont. Înregistrează-te ca utilizator diferit, încearcă să citești înregistrările altui utilizator direct prin API-ul REST. Dacă răspunsul este
200, politica este defectă. - Re-scanează. După aplicarea remedierii, rulează din nou o scanare FixVibe împotriva aceluiași URL. Rezultatul
baas.supabase-rlsar trebui să dispară.
-- Audit every table for missing RLS. Run in the Supabase SQL editor.
SELECT schemaname, tablename, rowsecurity
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY rowsecurity, tablename;Cum se compară acest scaner cu alte scanere
Majoritatea instrumentelor DAST generice (Burp Suite, OWASP ZAP, Nessus) nu știu ce este PostgREST. Vor parcurge aplicația ta, vor ignora calea /rest/v1/ și vor raporta despre paginile HTML pe care le înțeleg. Snyk și Semgrep sunt instrumente de analiză statică — găsesc fișierele de migrare din repo-ul tău cu apeluri RLS lipsă, dar nu pot dovedi că baza de date deployată este configurată greșit. FixVibe se află în această lacună: pasiv, conștient de BaaS, concentrat pe ceea ce un atacator neautentificat poate dovedi din URL-ul public.
Întrebări frecvente
Va citi sau modifica scanerul datele mele?
Nu. Scanările pasive emit cel mult un SELECT ... limit=1 per tabelă descoperită pentru a confirma dacă RLS permite citiri anonime. Scanerul înregistrează forma răspunsului, nu conținutul rândurilor. Sondele INSERT, UPDATE și DELETE sunt condiționate de proprietatea verificată a domeniului și nu rulează niciodată împotriva țintelor neverificate.
Funcționează dacă proiectul meu Supabase este pe pauză sau pe un domeniu personalizat?
Proiectele puse pe pauză returnează 503 la fiecare cerere — scanerul raportează proiectul ca fiind inaccesibil. Domeniile personalizate funcționează atâta timp cât aplicația deployată încă încarcă SDK-ul client Supabase în browser; scanerul extrage URL-ul proiectului din bundle în orice caz.
Ce se întâmplă dacă cheia anon este rotită sau cheia publicabilă se schimbă?
Re-rulează scanarea. Scanerul re-extrage cheia din bundle-ul curent la fiecare rulare. Rotația invalidează doar raportul anterior, nu starea politicii bazei de date.
Verifică scanerul noul model de chei publicabile Supabase (<code>sb_publishable_*</code>)?
Da. Detectorul recunoaște atât JWT-urile legacy anon cât și noile chei sb_publishable_* și le tratează identic — ambele sunt destinate să fie publice și ambele lasă RLS ca singura linie de apărare.
Pași următori
Rulează o scanare FixVibe gratuită împotriva URL-ului tău de producție — verificarea baas.supabase-rls este activată pe fiecare plan, inclusiv pe planul gratuit. Pentru o lectură mai aprofundată despre ce altceva poate scurge dintr-un proiect Supabase, vezi Cheia service role Supabase expusă în JavaScript și Lista de verificare pentru securitatea bucket-urilor de stocare Supabase. Pentru perspectiva generală asupra tuturor furnizorilor BaaS, citește Scaner de configurări greșite BaaS.
