FixVibe

// docs / baas security / supabase storage

Λίστα ελέγχου ασφάλειας bucket storage του Supabase: 22 σημεία

Το Supabase Storage είναι ένα λεπτό περιτύλιγμα γύρω από ένα bucket συμβατό με S3 συν το ίδιο μοντέλο Row-Level Security με τη βάση δεδομένων. Αυτό σημαίνει ότι οι ίδιες παγίδες RLS που επηρεάζουν τους πίνακες επηρεάζουν την πρόσβαση σε αρχεία — και μερικές ειδικές για storage που εμφανίζονται όταν τα εργαλεία κωδικοποίησης με ΤΝ συνδέουν uploads. Αυτή η λίστα ελέγχου είναι 22 στοιχεία σε πέντε ενότητες: ρύθμιση bucket, policies RLS, επικύρωση upload, signed URLs και λειτουργική υγιεινή. Καθένα είναι επαληθεύσιμο σε λιγότερο από 15 λεπτά.

Κάθε στοιχείο παρακάτω είναι ουσιώδες. Για τους υποκείμενους μηχανισμούς RLS, δες Supabase RLS scanner. Για την κατηγορία έκθεσης key γειτονική στο storage, δες Service role key του Supabase εκτεθειμένο σε JavaScript.

Ρύθμιση bucket

Ξεκίνα με τις σωστές προεπιλογές. Ένα λανθασμένα ρυθμισμένο bucket διαρρέει αρχεία είτε το RLS σου είναι σωστό είτε όχι.

  1. Όρισε κάθε bucket ως ιδιωτικό από προεπιλογή. Στο Supabase Dashboard → Storage → Buckets, βάλε τον διακόπτη Public bucket στο off εκτός αν έχεις ρητό λόγο (assets marketing, δημόσια avatars χωρίς PII). Τα δημόσια buckets παρακάμπτουν το RLS για λειτουργίες ανάγνωσης — οποιοσδήποτε με το όνομα του bucket μπορεί να κάνει list και download.
  2. Όρισε ένα σκληρό όριο μεγέθους αρχείου σε κάθε bucket. Dashboard → Bucket settings → File size limit. Τα 50 MB είναι λογική προεπιλογή για uploads χρηστών· αύξησέ το συνειδητά για περιπτώσεις χρήσης βίντεο / μεγάλων αρχείων. Χωρίς όριο, ένα μόνο κακόβουλο upload μπορεί να εξαντλήσει την ποσόστωση storage σου ή το μηνιαίο εύρος ζώνης.
  3. Περιόρισε τους επιτρεπόμενους τύπους MIME ανά bucket. Λίστα επιτρεπόμενων MIME — ρητή allowlist, όχι blocklist. image/jpeg, image/png, image/webp για buckets μόνο εικόνων. Ποτέ μην επιτρέπεις text/html, application/javascript ή image/svg+xml σε bucket περιεχομένου χρηστών — εκτελούνται στον browser όταν σερβίρονται μέσω signed URL.
  4. Χρησιμοποίησε ένα bucket ανά τύπο περιεχομένου, όχι ένα κοινόχρηστο bucket. Οι ρυθμίσεις ανά bucket (μέγεθος, MIME types, policies RLS) είναι η ευκρίνεια που έχεις. Ένα bucket user-avatars, ένα document-uploads και ένα public-assets είναι ευκολότερο να κλειδωθούν από ένα ανάμεικτο bucket.
  5. Επαλήθευσε τη ρύθμιση CORS εάν γίνονται uploads από το frontend. Εάν οι χρήστες ανεβάζουν απευθείας από τον browser σε ένα signed URL, το CORS του bucket πρέπει να αναφέρει την origin παραγωγής σου. Το * είναι αποδεκτό μόνο για δημόσια buckets — ποτέ για buckets που περιέχουν PII χρηστών.

Policies RLS στο storage.objects

Το Supabase Storage αποθηκεύει metadata αρχείων στον πίνακα storage.objects. Το RLS σε αυτόν τον πίνακα ελέγχει ποιος μπορεί να διαβάζει, να ανεβάζει, να ενημερώνει ή να διαγράφει αρχεία. Χωρίς RLS, το flag public/private του bucket είναι η μόνη σου προστασία.

  1. Επιβεβαίωσε ότι το RLS είναι ενεργοποιημένο στο storage.objects. Το SELECT rowsecurity FROM pg_tables WHERE schemaname = 'storage' AND tablename = 'objects'; πρέπει να επιστρέφει true. Το Supabase το ενεργοποιεί από προεπιλογή σε νέα project· επαλήθευσε ότι δεν έχει απενεργοποιηθεί.
  2. Γράψε μια policy SELECT εστιασμένη στο auth.uid() για ιδιωτικά buckets. CREATE POLICY "users_read_own_files" ON storage.objects FOR SELECT USING (auth.uid()::text = (storage.foldername(name))[1]);. Η σύμβαση είναι να αποθηκεύεις αρχεία κάτω από [user-id]/[filename] και να χρησιμοποιείς storage.foldername() για να εξάγεις τον ιδιοκτήτη από το path.
  3. Γράψε μια policy INSERT που επιβάλλει την ίδια σύμβαση διαδρομής. CREATE POLICY "users_upload_own" ON storage.objects FOR INSERT WITH CHECK (auth.uid()::text = (storage.foldername(name))[1]);. Χωρίς WITH CHECK, ένας πιστοποιημένος χρήστης μπορεί να ανεβάσει στον φάκελο άλλου χρήστη.
  4. Πρόσθεσε policies UPDATE και DELETE εάν η εφαρμογή σου υποστηρίζει επεξεργασίες ή διαγραφές αρχείων. Κάθε εντολή χρειάζεται τη δική της policy. Το να παραλείψεις το DELETE σημαίνει ότι οι πιστοποιημένοι χρήστες δεν μπορούν να αφαιρέσουν τα δικά τους αρχεία· το να παραλείψεις το UPDATE σημαίνει ότι οι αντικαταστάσεις αρχείων αποτυγχάνουν σιωπηλά.
  5. Δοκίμασε διασταυρωμένη πρόσβαση χρηστών σε δύο συνεδρίες browser. Συνδέσου ως Χρήστης Α, ανέβασε ένα αρχείο, αντιγράφος το path. Συνδέσου ως Χρήστης Β σε άλλον browser, προσπάθησε να φέρεις το αρχείο μέσω του REST API. Η απάντηση πρέπει να είναι 403 ή 404, ποτέ 200.
sql
-- Confirm RLS on storage.objects
SELECT rowsecurity
FROM   pg_tables
WHERE  schemaname = 'storage' AND tablename = 'objects';

-- SELECT policy: scope reads to the owning user's folder.
CREATE POLICY "users_read_own_files"
  ON storage.objects
  FOR SELECT
  USING (auth.uid()::text = (storage.foldername(name))[1]);

-- INSERT policy: enforce the [user-id]/[filename] path convention.
CREATE POLICY "users_upload_own"
  ON storage.objects
  FOR INSERT
  WITH CHECK (auth.uid()::text = (storage.foldername(name))[1]);

Επικύρωση upload

Επικύρωσε κάθε upload από την πλευρά του server, ακόμα και όταν το bucket έχει περιορισμούς MIME και μεγέθους. Τα εργαλεία κωδικοποίησης με ΤΝ παράγουν από προεπιλογή μόνο επικύρωση client· αυτό δεν προστατεύει τίποτα.

  1. Έλεγξε ξανά τον τύπο MIME από την πλευρά του server από τα πραγματικά bytes του αρχείου, όχι από τον header Content-Type. Χρησιμοποίησε μια βιβλιοθήκη όπως το file-type (Node) ή magic-byte sniffing. Ένας επιτιθέμενος μπορεί να ισχυριστεί Content-Type: image/jpeg σε ένα αρχείο που στην πραγματικότητα είναι ένα πολυγλωσσικό payload HTML / JavaScript.
  2. Αφαίρεσε τα metadata EXIF από τις ανεβασμένες εικόνες. Το EXIF μπορεί να περιέχει συντεταγμένες GPS, σειριακούς αριθμούς συσκευών και timestamps. Χρησιμοποίησε sharp με .withMetadata(false) ή exif-parser για να τα αφαιρέσεις πριν την αποθήκευση.
  3. Απόρριψε SVGs που περιέχουν tags script ή handlers onload. Το SVG είναι XML — και πολλές εφαρμογές που δημιουργούνται από ΤΝ επιτρέπουν uploads SVG ως «απλώς εικόνα». Χρησιμοποίησε DOMPurify από την πλευρά του server ή αρνήσου εντελώς uploads SVG.
  4. Χρησιμοποίησε ντετερμινιστικά, μη μαντεύσιμα ονόματα αρχείων. Μη διατηρείς το αρχικό όνομα αρχείου. Χρησιμοποίησε ένα UUID ή ένα hash του περιεχομένου του αρχείου. Τα αρχικά ονόματα αρχείων διαρρέουν («passport_scan_2024_01_15.jpg») και τα προβλέψιμα ονόματα επιτρέπουν απαρίθμηση.

Signed URLs

Τα signed URLs είναι ο τρόπος που οι clients προσπελαύνουν ιδιωτικά buckets. Η λήξη, το scope του bucket και τι καταγράφεται έχουν σημασία.

  1. Όρισε τη λήξη signed-URL ως προεπιλογή σε 1 ώρα ή λιγότερο. Η createSignedUrl(path, expiresIn) του Supabase JS SDK δέχεται δευτερόλεπτα. Ποτέ μη χρησιμοποιείς τιμές όπως 31536000 (ένα έτος) — το URL γίνεται ένας μόνιμα ημι-δημόσιος σύνδεσμος.
  2. Ποτέ μην αποθηκεύεις signed URLs στη βάση δεδομένων σου. Δημιούργησε φρέσκα από την πλευρά του server σε κάθε αίτημα. Ένα αποθηκευμένο signed URL με λήξη 1 έτους που διαρρέει μέσω ενός dump βάσης δεδομένων παραχωρεί μακροπρόθεσμη πρόσβαση.
  3. Κατάγραψε τη δημιουργία signed-URL, όχι μόνο τα uploads αρχείων. Εάν υποψιαστείς αργότερα παραβίαση, χρειάζεται να ξέρεις ποιος δημιούργησε ποιο URL πότε. Κατάγραψε auth.uid() + bucket + path αντικειμένου + timestamp.
  4. Χρησιμοποίησε την επιλογή downloadAs όταν σερβίρεις αρχεία ανεβασμένα από χρήστες. Το createSignedUrl(path, expiresIn, { download: '.jpg' }) επιβάλλει έναν header Content-Disposition: attachment ώστε το αρχείο να κατεβάζεται αντί να αποδίδεται — εξουδετερώνει την κατηγορία εκτέλεσης HTML / SVG / HTML-σε-PDF.

Λειτουργική υγιεινή

Η ρύθμιση storage μεταβάλλεται με τον χρόνο. Αυτά τα τέσσερα λειτουργικά στοιχεία κρατούν την επιφάνεια σφιχτή.

  1. Έλεγξε τα buckets τριμηνιαία. Dashboard → Storage → Buckets. Επιβεβαίωσε ότι η κατάσταση public/private και οι λίστες MIME types ταιριάζουν με αυτό που αναμένει η εφαρμογή. Τα buckets που δημιουργήθηκαν «προσωρινά» γίνονται μόνιμα εάν κανείς δεν τα αφαιρέσει.
  2. Παρακολούθησε ανώνυμες λειτουργίες list. Τα logs storage (Dashboard → Logs → Storage) καταγράφουν αιτήματα LIST. Μια ξαφνική αύξηση ανώνυμων αιτημάτων list εναντίον ιδιωτικού bucket σημαίνει ότι κάποιος το διερευνά από έξω.
  3. Όρισε policy διατήρησης για εφήμερα uploads. Τα προσωρινά buckets (προεπισκόπηση εικόνας, uploads προσχεδίων) πρέπει να αυτο-διαγράφονται μετά από 24-72 ώρες μέσω scheduled function. Η αόριστη διατήρηση είναι ευθύνη βάσει υποχρεώσεων ελαχιστοποίησης δεδομένων του GDPR / CCPA.
  4. Τρέξε μια σάρωση FixVibe μηνιαία. Ο έλεγχος baas.supabase-storage-public διερευνά buckets που ανταποκρίνονται σε ανώνυμα GET + LIST. Νέα buckets προστίθενται· παλιά αλλάζουν ορατότητα — μόνο η συνεχής σάρωση εντοπίζει τη μεταβολή.

Επόμενα βήματα

Τρέξε μια σάρωση FixVibe εναντίον του URL παραγωγής σου — οι ανώνυμες λίστες storage εμφανίζονται κάτω από baas.supabase-storage-public. Συνδύασε αυτή τη λίστα ελέγχου με Supabase RLS scanner για το επίπεδο πινάκων και Service role key του Supabase εκτεθειμένο σε JavaScript για τη γειτονική κατηγορία έκθεσης key. Για εσφαλμένες ρυθμίσεις storage σε άλλους παρόχους BaaS, δες Scanner εσφαλμένων ρυθμίσεων BaaS.

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

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

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

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

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

Λίστα ελέγχου ασφάλειας bucket storage του Supabase: 22 σημεία — Docs · FixVibe