FixVibe

// 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 δεν διαθέτει policy INSERT που το αρνείται — ακόμα και αν το 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.

sql
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.

sql
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. Στάδιο 1 — fingerprint. Ο scanner κάνει crawl την deployed εφαρμογή, αναλύει το JavaScript bundle της και εξάγει το URL του project Supabase και το anon key από τη ρύθμιση runtime. Χωρίς εικασίες DNS, χωρίς brute force — διαβάζει αυτό που διαβάζει ο browser.
  2. Στάδιο 2 — ανακάλυψη σχήματος. Ένα μοναδικό GET /rest/v1/ με το anon key επιστρέφει το σχήμα OpenAPI για κάθε πίνακα που μπορεί να δει ο ανώνυμος ρόλος. Ο scanner καταγράφει τα ονόματα πινάκων αλλά δεν διαβάζει δεδομένα γραμμών σε αυτό το στάδιο.
  3. Στάδιο 3 — διερευνητικές αναγνώσεις και εγγραφές. Για κάθε πίνακα που ανακαλύπτεται, ο scanner εκδίδει ένα ανώνυμο SELECT με limit=1. Εάν επιστρέψουν γραμμές, το RLS είναι ανεκτικό. Ο scanner σταματά εκεί — δεν απαριθμεί γραμμές, δεν κάνει pagination, δεν τροποποιεί δεδομένα. Οι διερευνητικές INSERT είναι φραγμένες πίσω από επαληθευμένη ιδιοκτησία τομέα και ρητή συγκατάθεση· δεν τρέχουν ποτέ εναντίον μη επαληθευμένων στόχων.

Κάθε εύρημα συνοδεύεται από το ακριβές URL αιτήματος, την κατάσταση της απάντησης, τη μορφή της απάντησης (μόνο headers) και το όνομα του πίνακα. Το prompt διόρθωσης ΤΝ στο κάτω μέρος του ευρήματος είναι ένα μπλοκ SQL για αντιγραφή-επικόλληση που τρέχεις στον editor SQL του Supabase.

Τι να κάνεις όταν ο scanner βρίσκει κάτι

Κάθε εύρημα RLS είναι έκτακτη ανάγκη runtime. Τα δημόσια endpoints PostgREST σαρώνονται από επιτιθέμενους μέσα σε λεπτά. Η αλληλουχία αποκατάστασης είναι μηχανική:

  1. Έλεγξε κάθε πίνακα. Τρέξε SELECT schemaname, tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public'; στον editor SQL του Supabase. Κάθε γραμμή με rowsecurity = false είναι πρόβλημα.
  2. Ενεργοποίησε RLS σε κάθε δημόσιο πίνακα. Όρισε ως προεπιλογή ENABLE ROW LEVEL SECURITY και FORCE ROW LEVEL SECURITY σε κάθε πίνακα που δημιουργείται — κάν' το πρότυπο migration.
  3. Γράψε policies εντολή προς εντολή. Μη χρησιμοποιείς FOR ALL USING (true). Γράψε ρητές policies για SELECT, INSERT, UPDATE, DELETE — καθεμία εστιασμένη στο auth.uid() ή σε μια στήλη org-id από το auth.jwt().
  4. Επαλήθευσε με έναν δεύτερο λογαριασμό. Εγγράψου ως διαφορετικός χρήστης, προσπάθησε να διαβάσεις τα αρχεία άλλου χρήστη απευθείας μέσω του REST API. Εάν η απάντηση είναι 200, η policy είναι χαλασμένη.
  5. Επανάλαβε τη σάρωση. Μετά την εφαρμογή της διόρθωσης, ξανατρέξε μια σάρωση FixVibe εναντίον του ίδιου URL. Το εύρημα baas.supabase-rls θα πρέπει να εξαφανιστεί.
sql
-- 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.

// σάρωσε την επιφάνεια baas σου

Βρες τον ανοιχτό πίνακα πριν τον βρει κάποιος άλλος.

Δώσε ένα URL παραγωγής. Το FixVibe απαριθμεί τους παρόχους BaaS με τους οποίους μιλάει η εφαρμογή σου, εντοπίζει τα δημόσια endpoints τους και αναφέρει τι μπορεί να διαβάσει ή να γράψει ένας μη πιστοποιημένος client. Δωρεάν, χωρίς εγκατάσταση, χωρίς κάρτα.

  • Δωρεάν επίπεδο — 3 σαρώσεις τον μήνα, χωρίς κάρτα στην εγγραφή.
  • Παθητικό fingerprinting BaaS — δεν απαιτείται επαλήθευση τομέα.
  • Supabase, Firebase, Clerk, Auth0, Appwrite και άλλα.
  • Prompts διόρθωσης με ΤΝ σε κάθε εύρημα — επικόλλησε τα ξανά στο Cursor / Claude Code.
Εκτέλεσε δωρεάν σάρωση BaaS

δεν απαιτείται εγγραφή

Supabase RLS scanner: βρες πίνακες με ελλιπή ή χαλασμένη row-level security — Docs · FixVibe