VIBE-LEAK SCANNER · SUPABASE TARGET: PUBLIC REST API AUTH: ANON KEY 16.326 EXPOSED SYNC…
data breach · ai-generated backends · row level security

VIBE-LEAK: 16.326 Ανοιχτές Βάσεις

Στις 25 Σεπτεμβρίου 2026 η UpGuard δημοσίευσε τη μεγαλύτερη μελέτη του είδους: από ~300.000 domains με Supabase, τα 16.326 είχαν πίνακες που διαβάζει όλο το internet — χωρίς login, χωρίς κλειδί, με ένα URL. Πάνω από τα μισά έδειχναν προσωπικά δεδομένα. Η αιτία δεν είναι hack. Είναι το default που ζει σε λάθος μέρος — και τα AI coding tools που παίρνουν πάντα τον δρόμο χωρίς αυτό. Αυτό είναι το πλήρες τεχνικό dossier: κλίμακα, μηχανισμός, lineage, και το audit που κλείνει την τρύπα σε 15 λεπτά.

$ RLS EXPOSURE LAB · 12-CONFIG MATRIX · 15-MIN AUDIT
Βάσεις με readable tables
16.326 από ~300.000 domains · floor, όχι ταβάνι
Με δείκτες PII
>50% ονόματα, emails, τηλέφωνα, διευθύνσεις
Με passwords/tokens
μειοψηφία συχνά plaintext · σπάνια κάρτες
Αποτίμηση Supabase
$10B Ιούνιος 2026 · vibe-coding backend
Enforcement νέων defaults
30/10 δεν αγγίζει existing tables

01 // Η κλίμακα: τι μέτρησε η UpGuard

Η μελέτη «Everything Everywhere» (UpGuard Research, Greg Pollock, 25/9/2026) είναι η μεγαλύτερη του είδους — και η μεθοδολογία της έχει μία λεπτομέρεια που την κάνει χειρότερη απ' όσο ακούγεται. Οι ερευνητές εντόπισαν domains με Supabase (BuiltWith + Chrome UX Report + API keys σε δημόσια JavaScript) και μετά ζήτησαν από κάθε βάση έναν πίνακα με συγκεκριμένο όνομα: users. Βάση που διαρρέει customers ή orders αλλά δεν έχει users δεν μετρήθηκε ποτέ. Το 16.326 είναι κατώφλι.

Αντί να διαβάσουν κάθε γραμμή (16 χιλιάδες βάσεις × άπειρα rows), ανέλυσαν schemas: ονόματα πινάκων και στηλών σαν δείκτες περιεχομένου. Όπου τα metadata έδειχναν σοβαρή έκθεση, το επαλήθευσαν δειγματοληπτικά — και ειδοποίησαν τους ιδιοκτήτες. Σωστή δεοντολογία, σπάνια σε τέτοια scale.

Η gallery των θυμάτων

US VALET SERVICE78.000 ΠΙΝΑΚΙΔΕΣ · 100.000 ΤΗΛΕΦΩΝΑ

43.000 ονόματα+emails, 665 employee records με 4.560 corporate emails και push tokens. Ποιος παρκάρει πού, πότε, με ποιο αμάξι — δημόσια.

IMMIGRATION COACHING · ΚΑΝΑΔΑΣ~5.000 RECORDS · 884 PLAINTEXT PASSWORDS

Ονόματα, emails, τηλέφωνα, ημερομηνίες γέννησης. Άνθρωποι σε ευάλωτη μεταναστευτική διαδικασία, με κωδικούς σε καθαρό κείμενο.

ΠΡΟΞΕΝΕΙΟ ΣΤΗ ΓΑΛΛΙΑ25.000 RECORDS · ΔΙΕΥΘΥΝΣΕΙΣ

Προσωπικά στοιχεία και φυσικές διευθύνσεις — μέσα και emergency housing ευάλωτων μεταναστών. Κρατική υπόθεση, ανοιχτή στο web.

SIM FARM · ΦΙΛΙΠΠΙΝΕΣ2.000+ USERS · 100.000+ SMS

Virtual SIM υποδομή με μηνύματα — μέσα και one-time passcodes. Υποδομή που κανονικά υπηρετεί bulk account-creation fraud, τώρα διάφανη.

ADULT PLATFORM · ΙΝΔΙΑPRIVATE CHATS ΟΛΟΚΛΗΡΑ

Ιδιωτικές συνομιλίες χρηστών-performers, πλήρως αναγνώσιμες. Το είδος έκθεσης που δεν «μαζεύεται» ποτέ πίσω.

Γεωγραφία και κλάδοι, όπως τα χαρτογράφησε η έκθεση: παγκόσμιο φαινόμενο, αλλά η Ευρώπη τα πάει καλύτερα (οι νόμοι προστασίας δεδομένων σπρώχνουν καλύτερες πρακτικές) και οι αναπτυσσόμενες περιοχές χειρότερα. Χειρότεροι κλάδοι: e-commerce και εστιατόρια (PII + payments)· πιο πιθανά να διαρρέουν passwords: μη αδειοδοτημένα στοιχηματικά sites. Το αν πουλάς σε καταναλωτές ή επιχειρήσεις δεν έκανε καμία διαφορά.

Το πόρισμα της UpGuard σε μία πρόταση: οι διαρροές είναι το πολλαπλασιαστικό γινόμενο της ευκολίας λανθασμένης ρύθμισης μιας τεχνολογίας επί το μέγεθος της βάσης χρηστών της. Το Supabase έκανε τη βάση πανεύκολη· τα AI tools έκαναν τους χρήστες εκατομμύρια. Το γινόμενο είναι 16.326 — τουλάχιστον.

02 // Ο μηχανισμός: το default που ζει σε λάθος μέρος

Το Supabase εκθέτει κάθε πίνακα σαν REST endpoint: yourproject.supabase.co/rest/v1/your_table. Γι αυτό το AI χτίζει app τόσο γρήγορα — δεν γράφει backend, ο browser μιλάει κατευθείαν στη βάση. Η γραμμή άμυνας είναι μία και μοναδική: Row Level Security policies σε κάθε πίνακα.

Δύο defaults, δύο κόσμοι

Φτιάχνεις πίνακα στο dashboard (Table Editor): το RLS ανάβει μόνο του. Τον φτιάχνεις με SQL, migration, CLI, psql — ή τον φτιάχνει agent: RLS off. Η λεπτομέρεια που τα εξηγεί όλα (reptile.haus, 26/9): το default είναι pre-ticked πεδίο σε React φόρμα (isRLSEnabled: true) — το table-creation API δεν έχει καν παράμετρο RLS. Δημιουργία και προστασία είναι δύο ξεχωριστές κλήσεις· ο agent κάνει πάντα μόνο την πρώτη. Το ασφαλές default δεν ζει στη βάση, ούτε στο API, ούτε στην πλατφόρμα. Ζει στην αρχική κατάσταση ενός component. Κάθε client που δεν το renderάρει κληρονομεί το Postgres default: ασφάλεια off.

Το anon key δεν είναι μυστικό — και αυτό είναι το point

Κάθε Supabase app κουβαλάει το publishable/anon key στο bundle — by design δημόσιο, η Supabase το λέει ρητά «safe to expose». Το κλειδί ταυτοποιεί το project, δεν εξουσιοδοτεί τον καλούντα. Η εξουσιοδότηση είναι το RLS. Με RLS off, το «δημόσιο» κλειδί είναι de facto master key:

ATTACK · BROWSER CONSOLENO LOGIN
// το key είναι ήδη στο bundle — το παίρνεις από το Network tab
const r = await fetch(
  'https://xyz.supabase.co/rest/v1/users?select=*',
  { headers: { apikey: 'PUBLIC_ANON_KEY',
               Authorization: 'Bearer PUBLIC_ANON_KEY' } }
);
console.log(await r.json()); // ← όλοι οι users, αν το RLS είναι off

Και το αντίστροφο λάθος είναι καταστροφή από την άλλη μεριά: το service_role key παρακάμπτει κάθε RLS και ζει μόνο σε backend. Αν το βρεις σε client bundle, δεν έχεις «θέμα» — έχεις διαρροή: rotate αμέσως, μεταφορά κλήσης σε server function. Κανόνας: grep -r service_role src/ — επιτρέπεται να εμφανίζεται μόνο σε server paths.

Τρία grants, μία παγίδα

Από τα ίδια τα docs της Supabase — ο πίνακας που πρέπει να ξέρεις απέξω:

RoleΠαίρνει αυτόματαΤι πρέπει να κρατήσει
anonselect, insert, update, deleteΜόνο ό,τι διαβάζει signed-out επισκέπτης
authenticatedselect, insert, update, deleteΜόνο operations που εκθέτει το app
service_roleselect, insert, update, deleteΠλήρης πρόσβαση · bypass RLS · server-side μόνο

Προσοχή στη λεπτομέρεια-φωτιά: τα policies δεν ανακαλούν grants. Πίνακας «προστατευμένος» μόνο με policies χαρίζει ακόμα insert στον anon αν δεν έκοψες ποτέ το grant. Και τα views, by default security definer (τρέχουν σαν τον owner postgres), παρακάμπτουν το RLS του υποκείμενου πίνακα — θέλουν security_invoker = true (Postgres 15+) αλλιώς διαρρέουν ό,τι «προστατεύεις».

Η φράση-κλειδί όλου του dossier: «destroying data stops the machine — exposing data prints a note». Το MCP server της Supabase σταματάει το destructive SQL μέχρι human confirm, αλλά για RLS off απλώς συμβουλεύει (string στο list_tables, do-not-auto-apply — γιατί το άναμμα χωρίς policy κλειδώνει την εφαρμογή). Καταστροφή = gate. Διαρροή = σημείωση, και μόνο αν ρωτήσεις. Αυτή είναι η default στάση σχεδόν κάθε agent integration.

03 // Lineage: από τα 170 στα 16.326

Το Σεπτέμβρη δεν ανακαλύφθηκε κάτι καινούργιο — μετρήθηκε σε κλίμακα κάτι γνωστό. Το χρονικό:

ΜελέτηΔείγμαΕύρημα
Turner (3/2025)1.645 Lovable apps170 ελαττωματικές · 303 endpoints → CVE-2025-48757
Modern Pentest107 YC startups28% εκθέτουν PII
Symbiotic1.072 vibe-coded apps39 readable με public key
Escape~1.400 apps175 βάσεις με PII
Red Access380.000 URLs5.000 προσβάσιμες · 2.000 διαρροές
Wiz (2/2026)Moltbook35k emails + 1,5M tokens · RLS πουθενά
UpGuard (9/2026)~300.000 domains16.326 · >50% PII

04 // Το fix-loop: πώς το AI «διορθώνει» την ασφάλεια

Το πιο δηλητηριώδες εύρημα δεν είναι τεχνικό — είναι συμπεριφορικό. Η Palo Alto Unit 42 το λέει «fix loop»: ο agent γράφει query, η Postgres απαντά 42501 insufficient_privilege (το RLS κάνει σωστά τη δουλειά του), και ο agent το «φτιάχνει» ρίχνοντας το RLS. Το query δουλεύει. Ο πίνακας είναι ορθάνοιχτος. Το Carnegie Mellon το ποσοτικοποιεί: 61% του AI κώδικα λειτουργικά σωστό, μόνο 10,5% ασφαλές. Και 34% των vibe apps βγαίνουν χωρίς ούτε CSP/HSTS/clickjacking headers.

Η θεατρική ασφάλεια: 5 μοτίβα που περνάνε το «RLS on»

  1. Το ξεχασμένο related table. RLS στο todos, τίποτα στα todo_tags / comments / attachments — η διαρροή περνάει από δίπλα.
  2. USING (true). Policy που ισχύει για όλους = καθόλου policy. Το AI το γεννάει όταν δεν καταλαβαίνει το ownership.
  3. Έλεγχος μόνο auth.role() = 'authenticated'. Αποδεικνύει login, όχι ιδιοκτησία — κάθε user βλέπει κάθε row.
  4. UPDATE χωρίς WITH CHECK. Ο user αλλάζει το user_id της δικής του γραμμής και γίνεται «κάποιος άλλος» — κλασικό IDOR.
  5. Views χωρίς security_invoker + service_role στο bundle. Το πρώτο παρακάμπτει σιωπηλά, το δεύτερο φωναχτά.
SECURE PATTERN · OWNER-SCOPED (4 POLICIES)COPY-PASTE
ALTER TABLE notes ENABLE ROW LEVEL SECURITY;

CREATE POLICY "read own" ON notes FOR SELECT TO authenticated
  USING ((select auth.uid()) = user_id);
CREATE POLICY "insert own" ON notes FOR INSERT TO authenticated
  WITH CHECK ((select auth.uid()) = user_id);
CREATE POLICY "update own" ON notes FOR UPDATE TO authenticated
  USING ((select auth.uid()) = user_id)
  WITH CHECK ((select auth.uid()) = user_id);
CREATE POLICY "delete own" ON notes FOR DELETE TO authenticated
  USING ((select auth.uid()) = user_id);

Δύο λεπτομέρειες που αξίζουν: το (select auth.uid()) με παρένθεση αφήνει τον planner να το υπολογίσει μία φορά ανά query (performance σε μεγάλους πίνακες), και το TO authenticated κρατάει τον anon εντελώς έξω — ακριβώς ό,τι θέλεις για ιδιωτικά δεδομένα.

05 // LAB 1 · RLS Exposure Simulator

Προσομοιωμένο Supabase endpoint — συνθετικά δεδομένα, τίποτα αληθινό. Διάλεξε πίνακα, ρόλο και κατάσταση RLS, πάτα RUN και δες τι επιστρέφει το API. Αυτό που βλέπεις είναι ακριβώς αυτό που είδε η UpGuard, σε μικρογραφία.

$ RLS EXPOSURE SIMULATOR SYNTHETIC DATA · ΠΡΟΣΟΜΟΙΩΣΗ
TABLE
ROLE
ROW LEVEL SECURITY
GET /rest/v1/users?select=* ← πάτα RUN QUERY

06 // LAB 2 · 12-Config Matrix

Αναπαράσταση του τεστ 12 διαμορφώσεων (creativeainews): 4 διαρρέουν, 2 επιτρέπουν υπέρβαση, 6 κρατάνε. Πάτα κάθε config και δες την ετυμηγορία — μάντεψε πρώτα, μετά επαλήθευσε.

$ CONFIG MATRIX · 12 SETUPS ΠΗΓΗ: ΑΝΑΠΑΡΑΣΤΑΣΗ ΔΟΚΙΜΩΝ
ΔΙΑΛΕΞΕ CONFIG

12 κουμπιά, 12 μοίρες του ίδιου πίνακα. Οι μισές και πάνω «δουλεύουν» — λίγες προστατεύουν.

07 // Το 15λεπτο audit (και τι δεν φτιάχνει η 30/10)

Τρία βήματα, δεκαπέντε λεπτά, μηδενικές δικαιολογίες:

  1. Security Advisor. Dashboard ή supabase db advisors — κάθε ERROR είναι live διαρροή: 0013 table δημόσια προσβάσιμο · 0010 view παρακάμπτει RLS · 0016 materialized view εκτεθειμένο · 0023 ευαίσθητα δημόσια · 0024 policy ανοιχτή σε όλους.
  2. Το 5-line query. Ποιες στήλες ταξινομούνται πρώτες είναι οι κακές (RLS off, ή RLS on με μηδέν policies):
5-LINE AUDIT · SQL EDITORCOPY-PASTE
select n.nspname as schema, c.relname as table_name,
       c.relrowsecurity as rls_enabled,
       (select count(*) from pg_policy p where p.polrelid = c.oid) as policies
from pg_class c join pg_namespace n on n.oid = c.relnamespace
where c.relkind = 'r' and n.nspname not in ('pg_catalog','information_schema')
order by c.relrowsecurity, policies;
  1. Το curl της αλήθειας. Logged-out request με το anon key σε κάθε table με δεδομένα: rows = incident · [] ή error = σωστό. Μετά cross-account (B διαβάζει A;) και owner-check (ο νόμιμος δουλεύει;).

Checklist πριν το επόμενο deploy

Για την 30ή Οκτωβρίου, καθαρά: σταματάει τις νέες διαρροές (νέα tables θέλουν ρητό grant). Δεν αγγίζει existing tables — κρατάνε grants και μένουν προσβάσιμα. Ό,τι διαρρέει σήμερα θα διαρρέει και 31/10. Το email που θα στείλει η Supabase αφορά το μέλλον· το παρελθόν θέλει το query από πάνω.

08 // Ελλάδα: τι ισχύει εδώ

Ξεκάθαρο: η UpGuard δεν κατονόμασε ελληνικά θύματα — τα δείγματα είναι ΗΠΑ, Καναδάς, Γαλλία, Φιλιππίνες, Ινδία. Αυτό δεν είναι άλλοθι: η μέθοδος αφήνει εκτός ό,τι δεν λέγεται users, και δεν χαρτογράφησε ανά χώρα. Αν τρέχεις Supabase που το έφτιαξε agent, υπέθεσε έκθεση μέχρι να τρέξεις το audit.

Δύο ελληνικά hooks: πρώτον, η έκθεση σημειώνει ότι η Ευρώπη τα πάει καλύτερα λόγω νομοθεσίας — το GDPR (72ωρο breach notification, data minimisation) είναι η μόνη «default» που δουλεύει όταν τα τεχνικά defaults αποτυγχάνουν. Δημόσια αναγνώσιμη βάση με Έλληνες users = περιστατικό με ρολόι. Δεύτερον, αν έφτιαξες το project σου με οδηγό σαν τον δικό μας vibe-coding οδηγό, ξαναπέρασέ το με το checklist της ενότητας 07 — το «δουλεύει» δεν ήταν ποτέ απόδειξη ότι «προστατεύει».

Ο κανόνας της μίας πρότασης

Στο Supabase, ο πίνακας είναι δημόσιος μέχρι η policy να πει ότι δεν είναι — και το AI δεν θα γράψει αυτή την policy αξιόπιστα. Νέα συνήθεια: κάθε table που δημιουργείται, RLS + policies πριν βγει η οθόνη που το χρησιμοποιεί. Τέσσερα SQL statements ανά πίνακα (ενότητα 04) — η διαφορά ανάμεσα σε launch και σε εμφάνιση στην επόμενη μελέτη.

09 // Συχνές ερωτήσεις

Το anon key του Supabase είναι μυστικό;

Όχι — είναι δημόσιο by design και ζει σε κάθε frontend bundle. Η ασφάλεια δεν είναι η απόκρυψη του κλειδιού αλλά οι RLS policies: το κλειδί φτάνει μόνο ό,τι επιτρέπει το RLS. Με RLS off, το «δημόσιο» κλειδί γίνεται de facto master key. Αντίθετα το service_role key παρακάμπτει κάθε RLS και δεν πρέπει να βγαίνει ποτέ στον browser — αν το βρεις σε client code, θεώρησέ το διαρροή και κάνε rotate αμέσως.

Γιατί τα AI tools φτιάχνουν βάσεις χωρίς RLS;

Γιατί το ασφαλές default ζει σε React component του dashboard (Table Editor), όχι στο API — το table-creation API δεν έχει καν παράμετρο RLS. Ο agent γράφει SQL (create table) και φεύγει· η προστασία είναι δεύτερη κλήση που κανείς δεν του ζήτησε. Το dashboard κάνει και τα δύο βήματα, ο agent μόνο το πρώτο.

Η αλλαγή της 30ής Οκτωβρίου τα φτιάχνει όλα;

Όχι. Από 30/10/2026 τα νέα tables χρειάζονται ρητό grant για να φανούν στο API — σταματάει τις νέες διαρροές. Αλλά τα existing tables κρατάνε τα grants τους και μένουν προσβάσιμα. Ό,τι διαρρέει σήμερα θα διαρρέει και 31/10 αν δεν βάλεις RLS χειροκίνητα.

Αρκεί να ανάψω το RLS;

Όχι — θέλει και policies, μία ανά operation (SELECT/INSERT/UPDATE/DELETE), αλλιώς κλειδώνεις και την ίδια σου την εφαρμογή. Και προσοχή στις θεατρικές policies: USING (true) για όλους, έλεγχος μόνο auth.role(), UPDATE χωρίς WITH CHECK, ξεχασμένα related tables και views χωρίς security_invoker — όλα περνάνε το «RLS on» και αποτυγχάνουν στον έλεγχο.

Υπάρχουν ελληνικά θύματα;

Η UpGuard δεν κατονόμασε ελληνικές περιπτώσεις — τα δείγματα που δημοσίευσε είναι ΗΠΑ, Καναδάς, Γαλλία, Φιλιππίνες, Ινδία. Αυτό δεν σημαίνει απουσία: η μέθοδος (probe μόνο για table users) αφήνει εκτός ό,τι λέγεται αλλιώς, και ο σαρωτής δεν χαρτογράφησε ανά χώρα. Αν τρέχεις Supabase φτιαγμένο από agent, υπέθεσε έκθεση μέχρι να το ελέγξεις.

Τι κάνω σε 15 λεπτά από τώρα;

Τρέξε Security Advisor (0013, 0010, 0016, 0023, 0024), το 5-line audit query για RLS/policies, και το curl ?select=* ως anon σε κάθε table με δεδομένα. Μετά: RLS + policies + grants στο ίδιο migration, security_invoker στα views, service_role εκτός browser, ensure_rls trigger για τα μελλοντικά, και splinter linter στο CI. Λεπτομέρειες στην ενότητα 07.

Vibe Coding: Ο Απόλυτος Οδηγός 2026BUILD · AI CODING ChainDrop: το npm worm σε Claude CodeSUPPLY CHAIN GitSpawn: εκτέλεση πριν ξυπνήσει το AIAGENTS · RCE Blackroom: επιθέσεις supply chainDEEP TECH

Πηγές & τεκμηρίωση

PRIMARYUpGuard Research «Everything Everywhere» (Greg Pollock, 25/9/2026) — ~300k domains, 16.326 readable, >50% PII, ειδοποίηση owners · Supabase CISO Bil Harmer via TechCrunch («secure by default», shared responsibility).
LINEAGEMatt Turner (3/2025): 1.645 apps → 170, 303 endpoints · NIST CVE-2025-48757 (29–30/5/2025, CVSS 9.3, disputed) · Modern Pentest 28% · Symbiotic 39/1.072 · Escape 175/1.400 · Red Access 2k/380k · Wiz Moltbook (2/2026): 35k emails + 1,5M tokens.
MECHANISMreptile.haus (26/9): form-component default, no-API-param, MCP asymmetry · Supabase Docs (grants table, security_invoker, service_role) · creativeainews 12-config matrix · Palo Alto Unit 42 fix-loop · Carnegie Mellon 61%/10,5%.
DATESChangelog 28/4 (opt-out) → 30/5 default νέων projects → 30/10 enforcement existing (χωρίς existing tables).
CAVEATΤο 16.326 είναι floor (probe μόνο users). Τα θύματα-δείγματα είναι όσα δημοσίευσε η UpGuard — καμία ελληνική περίπτωση κατονομασμένη. Τα labs τρέχουν συνθετικά δεδομένα, όχι πραγματικό endpoint.