// docs / baas security / supabase rls scanner
Supabase RLS scanner: βρες πίνακες με ελλιπή ή χαλασμένη row-level security
Το row-level security (RLS) είναι το μόνο πράγμα που στέκεται ανάμεσα στα δεδομένα των πελατών σου και το διαδίκτυο όταν δημοσιεύεις μια εφαρμογή που βασίζεται σε Supabase. Τα εργαλεία κωδικοποίησης με ΤΝ παράγουν κώδικα σε μορφή RLS που γίνεται compile, αποστέλλεται και διαρρέει σιωπηλά δεδομένα — πίνακες χωρίς ενεργοποιημένο RLS, policies που διαβάζουν αλλά δεν περιορίζουν ποτέ, predicates που συγκρίνουν μια στήλη με τον εαυτό της. Αυτό το άρθρο δείχνει τι μπορεί να αποδείξει ένα Supabase RLS scanner από έξω, τις τέσσερις μορφές χαλασμένου RLS που εμφανίζονται σε vibe-coded εφαρμογές και πώς να σαρώσεις το δικό σου deployment σε λιγότερο από ένα λεπτό.
Τι μπορεί να αποδείξει μια εξωτερική σάρωση RLS
Μια παθητική σάρωση RLS τρέχει εναντίον του endpoint PostgREST που εκθέτει το Supabase στο https://[project].supabase.co/rest/v1/. Χρησιμοποιεί μόνο το δημοσιεύσιμο anon key — το ίδιο key που χρησιμοποιεί ο browser σου — και ψάχνει για metadata λίστας πινάκων, ανώνυμες αναγνώσεις και ανώνυμες εγγραφές. Δεν πιστοποιείται ποτέ ως χρήστης και δεν αγγίζει ποτέ προνόμια service-role. Ό,τι μπορεί να κάνει, μπορεί να το κάνει και ένας μη πιστοποιημένος επιτιθέμενος στο διαδίκτυο.
Από έξω από τη βάση δεδομένων, ένας scanner μπορεί να επιβεβαιώσει τα ακόλουθα με υψηλή βεβαιότητα:
- Το RLS είναι απενεργοποιημένο σε έναν πίνακα. Το PostgREST επιστρέφει γραμμές για ένα ανώνυμο
SELECTόταν το RLS είναι κλειστό ή όταν μια policy το επιτρέπει. Και στις δύο περιπτώσεις πρόκειται για εύρημα. - Ο ανώνυμος ρόλος μπορεί να απαριθμήσει πίνακες. Ένα
GET /rest/v1/με το anon key επιστρέφει το σχήμα OpenAPI για κάθε πίνακα στον οποίο ο ρόλοςanonέχει οποιοδήποτε προνόμιο. Οι εφαρμογές που δημιουργούνται από ΤΝ συχνά παραχωρούνUSAGEστο σχήμα καιSELECTσε κάθε πίνακα, εκθέτοντας ολόκληρο τον χάρτη του σχήματος ακόμα και όταν το RLS αρνείται τις πραγματικές αναγνώσεις. - Ο ανώνυμος ρόλος μπορεί να κάνει insert. Ένα διερευνητικό
POSTμε μια εκτίμηση για τη μορφή των στηλών θα πετύχει εάν το RLS δεν διαθέτει policyINSERTπου το αρνείται — ακόμα και αν τοSELECTείναι κλειδωμένο. - Το service-role key βρίσκεται στο bundle του browser. Γειτονικό προς το RLS: εάν ένας scanner βρει
SUPABASE_SERVICE_ROLE_KEYή οποιοδήποτε JWT μεrole: service_roleστο JavaScript bundle, το RLS είναι ανίσχυρο — όποιος κατέχει αυτό το key παρακάμπτει κάθε policy.
Τι δεν μπορεί να αποδείξει μια εξωτερική σάρωση
Να είσαι ειλικρινής σχετικά με τα όρια του scanner. Μια εξωτερική σάρωση RLS δεν μπορεί να διαβάσει τον πίνακα pg_policies, τα αρχεία migration ή το ακριβές predicate οποιασδήποτε policy. Συμπεραίνει από συμπεριφορά μαύρου κουτιού, που σημαίνει ότι μερικές φορές θα αναφέρει ένα εύρημα που τελικά είναι σκόπιμα δημόσια δεδομένα (πίνακας newsletter marketing, δημόσιος κατάλογος προϊόντων). Η αναφορά του FixVibe επισημαίνει αυτά ως μέτρια βεβαιότητα όταν ο scanner δεν μπορεί να αποσαφηνίσει την πρόθεση — εξέτασε το όνομα του πίνακα και αποφάσισε.
Οι τέσσερις μορφές χαλασμένου RLS που παράγουν τα εργαλεία ΤΝ
Όταν στρέφεις τα Cursor, Claude Code, Lovable ή Bolt προς το Supabase, τα ίδια τέσσερα μοτίβα χαλασμένου RLS εμφανίζονται σε χιλιάδες εφαρμογές. Κάθε ένα περνά τον έλεγχο τύπων, κάνει compile και αποστέλλεται:
Μορφή 1: Το RLS δεν ενεργοποιήθηκε ποτέ
Ο πιο συνηθισμένος τρόπος αποτυχίας. Το migration δημιουργεί τον πίνακα αλλά ο προγραμματιστής (ή το εργαλείο ΤΝ) ξεχνά το ALTER TABLE ... ENABLE ROW LEVEL SECURITY. Το PostgREST σερβίρει χαρούμενα ολόκληρο τον πίνακα σε οποιονδήποτε έχει το anon key. Διόρθωση: ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY; ALTER TABLE public.[name] FORCE ROW LEVEL SECURITY;. Το FORCE δεν είναι προαιρετικό — χωρίς αυτό, ο ιδιοκτήτης του πίνακα (και κάθε ρόλος με ιδιοκτησία πίνακα) παρακάμπτει το RLS.
ALTER TABLE public.[name] ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.[name] FORCE ROW LEVEL SECURITY;Μορφή 2: Ενεργοποιημένο RLS, καμία policy
Μια πιο διακριτική αποτυχία. Το RLS είναι ενεργοποιημένο αλλά δεν έχουν γραφτεί policies. Η προεπιλογή στην PostgreSQL είναι άρνηση, οπότε οι πιστοποιημένοι χρήστες δεν βλέπουν τίποτα — και ο προγραμματιστής προσθέτει USING (true) για να λειτουργήσει η εφαρμογή, κάτι που επιτρέπει σε όλους να διαβάζουν τα πάντα. Διόρθωση: γράψε μια policy που εστιάζει μέσω auth.uid(): CREATE POLICY "select_own" ON public.[name] FOR SELECT USING (auth.uid() = user_id); και μια αντίστοιχη policy INSERT/UPDATE/DELETE.
CREATE POLICY "select_own"
ON public.[name]
FOR SELECT
USING (auth.uid() = user_id);Μορφή 3: Η policy συγκρίνει στήλη με τον εαυτό της
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.
Μορφή 4: Policy στο SELECT αλλά όχι στα INSERT/UPDATE
Ο προγραμματιστής κλειδώνει τις αναγνώσεις αλλά ξεχνά τις εγγραφές. Οι policies RLS είναι ανά εντολή. Το FOR SELECT προστατεύει μόνο τις αναγνώσεις· ένας ανώνυμος client μπορεί ακόμα να κάνει INSERT εάν καμία policy δεν το αρνείται. Διόρθωση: γράψε μια policy ανά εντολή ή χρησιμοποίησε FOR ALL με ρητές προτάσεις USING και WITH CHECK.
Πώς λειτουργεί ο Supabase RLS scanner του FixVibe
Ο έλεγχος baas.supabase-rls τρέχει σε τρία στάδια, καθένα με ρητά επίπεδα βεβαιότητας:
- Στάδιο 1 — fingerprint. Ο scanner κάνει crawl την deployed εφαρμογή, αναλύει το JavaScript bundle της και εξάγει το URL του project Supabase και το anon key από τη ρύθμιση runtime. Χωρίς εικασίες DNS, χωρίς brute force — διαβάζει αυτό που διαβάζει ο browser.
- Στάδιο 2 — ανακάλυψη σχήματος. Ένα μοναδικό
GET /rest/v1/με το anon key επιστρέφει το σχήμα OpenAPI για κάθε πίνακα που μπορεί να δει ο ανώνυμος ρόλος. Ο scanner καταγράφει τα ονόματα πινάκων αλλά δεν διαβάζει δεδομένα γραμμών σε αυτό το στάδιο. - Στάδιο 3 — διερευνητικές αναγνώσεις και εγγραφές. Για κάθε πίνακα που ανακαλύπτεται, ο scanner εκδίδει ένα ανώνυμο
SELECTμεlimit=1. Εάν επιστρέψουν γραμμές, το RLS είναι ανεκτικό. Ο scanner σταματά εκεί — δεν απαριθμεί γραμμές, δεν κάνει pagination, δεν τροποποιεί δεδομένα. Οι διερευνητικές INSERT είναι φραγμένες πίσω από επαληθευμένη ιδιοκτησία τομέα και ρητή συγκατάθεση· δεν τρέχουν ποτέ εναντίον μη επαληθευμένων στόχων.
Κάθε εύρημα συνοδεύεται από το ακριβές URL αιτήματος, την κατάσταση της απάντησης, τη μορφή της απάντησης (μόνο headers) και το όνομα του πίνακα. Το prompt διόρθωσης ΤΝ στο κάτω μέρος του ευρήματος είναι ένα μπλοκ SQL για αντιγραφή-επικόλληση που τρέχεις στον editor SQL του Supabase.
Τι να κάνεις όταν ο scanner βρίσκει κάτι
Κάθε εύρημα RLS είναι έκτακτη ανάγκη runtime. Τα δημόσια endpoints PostgREST σαρώνονται από επιτιθέμενους μέσα σε λεπτά. Η αλληλουχία αποκατάστασης είναι μηχανική:
- Έλεγξε κάθε πίνακα. Τρέξε
SELECT schemaname, tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public';στον editor SQL του Supabase. Κάθε γραμμή μεrowsecurity = falseείναι πρόβλημα. - Ενεργοποίησε RLS σε κάθε δημόσιο πίνακα. Όρισε ως προεπιλογή
ENABLE ROW LEVEL SECURITYκαιFORCE ROW LEVEL SECURITYσε κάθε πίνακα που δημιουργείται — κάν' το πρότυπο migration. - Γράψε policies εντολή προς εντολή. Μη χρησιμοποιείς
FOR ALL USING (true). Γράψε ρητές policies για SELECT, INSERT, UPDATE, DELETE — καθεμία εστιασμένη στοauth.uid()ή σε μια στήλη org-id από τοauth.jwt(). - Επαλήθευσε με έναν δεύτερο λογαριασμό. Εγγράψου ως διαφορετικός χρήστης, προσπάθησε να διαβάσεις τα αρχεία άλλου χρήστη απευθείας μέσω του REST API. Εάν η απάντηση είναι
200, η policy είναι χαλασμένη. - Επανάλαβε τη σάρωση. Μετά την εφαρμογή της διόρθωσης, ξανατρέξε μια σάρωση FixVibe εναντίον του ίδιου URL. Το εύρημα
baas.supabase-rlsθα πρέπει να εξαφανιστεί.
-- 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;Πώς συγκρίνεται αυτό με άλλους scanners
Τα περισσότερα γενικά εργαλεία DAST (Burp Suite, OWASP ZAP, Nessus) δεν γνωρίζουν τι είναι το PostgREST. Θα κάνουν crawl την εφαρμογή σου, θα αγνοήσουν το μονοπάτι /rest/v1/ και θα αναφέρουν για τις σελίδες HTML που καταλαβαίνουν. Τα Snyk και Semgrep είναι εργαλεία στατικής ανάλυσης — βρίσκουν αρχεία migration στο repo σου με ελλιπείς κλήσεις RLS, αλλά δεν μπορούν να αποδείξουν ότι η deployed βάση δεδομένων είναι λανθασμένα ρυθμισμένη. Το FixVibe κάθεται σε αυτό το κενό: παθητικό, ενήμερο για BaaS, εστιασμένο σε αυτό που μπορεί να αποδείξει ένας μη πιστοποιημένος επιτιθέμενος από το δημόσιο URL.
Συχνές ερωτήσεις
Θα διαβάσει ή θα τροποποιήσει τα δεδομένα μου ο scanner;
Όχι. Οι παθητικές σαρώσεις εκδίδουν το πολύ ένα SELECT ... limit=1 ανά πίνακα που ανακαλύπτεται, για να επιβεβαιώσουν εάν το RLS επιτρέπει ανώνυμες αναγνώσεις. Ο scanner καταγράφει τη μορφή της απάντησης, όχι το περιεχόμενο των γραμμών. Οι διερευνήσεις INSERT, UPDATE και DELETE είναι φραγμένες πίσω από επαληθευμένη ιδιοκτησία τομέα και δεν τρέχουν ποτέ εναντίον μη επαληθευμένων στόχων.
Λειτουργεί αυτό εάν το Supabase project μου είναι σε παύση ή σε προσαρμοσμένο τομέα;
Τα project σε παύση επιστρέφουν 503 σε κάθε αίτημα — ο scanner αναφέρει το project ως μη προσβάσιμο. Οι προσαρμοσμένοι τομείς λειτουργούν εφόσον η deployed εφαρμογή φορτώνει ακόμα το Supabase client SDK στον browser· ο scanner εξάγει το URL του project από το bundle έτσι κι αλλιώς.
Τι συμβαίνει εάν το anon key μου ανανεωθεί ή το publishable key μου αλλάξει;
Ξανατρέξε τη σάρωση. Ο scanner εξάγει εκ νέου το key από το τρέχον bundle σε κάθε εκτέλεση. Η ανανέωση ακυρώνει μόνο την προηγούμενη αναφορά, όχι την κατάσταση policy της βάσης δεδομένων.
Ελέγχει ο scanner το νέο μοντέλο publishable-key του Supabase (<code>sb_publishable_*</code>);
Ναι. Ο ανιχνευτής αναγνωρίζει τόσο τα παλιά anon JWTs όσο και τα νεότερα sb_publishable_* keys και τα αντιμετωπίζει με τον ίδιο τρόπο — και τα δύο προορίζονται να είναι δημόσια και και τα δύο αφήνουν το RLS ως τη μόνη γραμμή άμυνας.
Επόμενα βήματα
Τρέξε μια δωρεάν σάρωση FixVibe εναντίον του URL παραγωγής σου — ο έλεγχος baas.supabase-rls είναι ενεργοποιημένος σε κάθε πλάνο συμπεριλαμβανομένου του δωρεάν επιπέδου. Για βαθύτερη ανάγνωση σχετικά με το τι άλλο μπορεί να διαρρεύσει από ένα Supabase project, δες Service role key του Supabase εκτεθειμένο σε JavaScript και Λίστα ελέγχου ασφάλειας bucket storage του Supabase. Για τη συνολική εικόνα σε όλους τους παρόχους BaaS, διάβασε Scanner εσφαλμένων ρυθμίσεων BaaS.
