FixVibe

// docs / baas security / supabase storage

Supabase opslagbucket-beveiligingschecklist: 22 punten

Supabase Storage is een dunne wrapper rond een S3-compatibele bucket plus hetzelfde Row-Level Security-model als de database. Dat betekent dat dezelfde RLS-valkuilen die tabellen beïnvloeden ook bestandstoegang beïnvloeden — en een paar opslag-specifieke die opduiken wanneer AI-codeertools uploads bedraden. Deze checklist is 22 punten verdeeld over vijf secties: bucketconfiguratie, RLS-beleid, uploadvalidatie, getekende URL's en operationele hygiëne. Elk is verifieerbaar in minder dan 15 minuten.

Elk item hieronder is essentieel. Voor de onderliggende RLS-mechanica, zie Supabase RLS-scanner. Voor de sleutel-blootstellingsklasse aangrenzend aan opslag, zie Supabase service-rolsleutel blootgesteld in JavaScript.

Bucketconfiguratie

Begin met de juiste standaardwaarden. Een verkeerd geconfigureerde bucket lekt bestanden ongeacht of je RLS correct is of niet.

  1. Maak elke bucket standaard privé. Zet in het Supabase-dashboard → Storage → Buckets de Public bucket-schakelaar uit, tenzij je een expliciete reden hebt (marketingmateriaal, openbare avatars zonder PII). Openbare buckets omzeilen RLS voor leesoperaties — iedereen met de bucketnaam kan oplijsten en downloaden.
  2. Stel een harde bestandsgroottelimiet in op elke bucket. Dashboard → Bucket-instellingen → Bestandsgroottelimiet. 50 MB is een redelijke standaard voor gebruikersuploads; verhoog opzettelijk voor video / grote-bestand-use cases. Zonder limiet kan een enkele kwaadaardige upload je opslagquotum of je maandelijkse bandbreedte uitputten.
  3. Beperk toegestane MIME-types per bucket. Lijst van toegestane MIME-types — expliciete allowlist, geen blocklist. image/jpeg, image/png, image/webp voor alleen-afbeeldingen-buckets. Sta nooit text/html, application/javascript of image/svg+xml toe in een gebruikersinhoud-bucket — ze voeren uit in de browser wanneer ze via getekende URL worden geserveerd.
  4. Gebruik één bucket per inhoudstype, niet één gedeelde bucket. Per-bucket-instellingen (grootte, MIME-types, RLS-beleid) zijn de granulariteit die je hebt. Een user-avatars-bucket, een document-uploads-bucket en een public-assets-bucket zijn gemakkelijker te vergrendelen dan één gemengde bucket.
  5. Verifieer CORS-configuratie als de frontend uploadt. Als gebruikers direct vanuit de browser uploaden naar een getekende URL, moet de bucket-CORS je productie-origin vermelden. * is alleen acceptabel voor openbare buckets — nooit voor buckets die gebruikers-PII bevatten.

RLS-beleid op storage.objects

Supabase Storage slaat bestandsmetadata op in de storage.objects-tabel. RLS op die tabel bepaalt wie bestanden kan lezen, uploaden, bijwerken of verwijderen. Zonder RLS is de openbare/private vlag van de bucket je enige bescherming.

  1. Bevestig dat RLS is ingeschakeld op storage.objects. SELECT rowsecurity FROM pg_tables WHERE schemaname = 'storage' AND tablename = 'objects'; moet true retourneren. Supabase schakelt het standaard in op nieuwe projecten; verifieer dat het niet is uitgeschakeld.
  2. Schrijf een SELECT-beleid gescoopt op auth.uid() voor privé buckets. CREATE POLICY "users_read_own_files" ON storage.objects FOR SELECT USING (auth.uid()::text = (storage.foldername(name))[1]);. De conventie is om bestanden op te slaan onder [user-id]/[filename] en storage.foldername() te gebruiken om de eigenaar uit het pad te extraheren.
  3. Schrijf een INSERT-beleid dat dezelfde padconventie afdwingt. CREATE POLICY "users_upload_own" ON storage.objects FOR INSERT WITH CHECK (auth.uid()::text = (storage.foldername(name))[1]);. Zonder WITH CHECK kan een geauthenticeerde gebruiker uploaden in de map van een andere gebruiker.
  4. Voeg UPDATE- en DELETE-beleid toe als je app bestandsbewerkingen of verwijderingen ondersteunt. Elke opdracht heeft zijn eigen beleid nodig. DELETE overslaan betekent dat geauthenticeerde gebruikers hun eigen bestanden niet kunnen verwijderen; UPDATE overslaan betekent dat bestand-overschrijvingen stil mislukken.
  5. Test cross-user toegang in twee browsersessies. Log in als gebruiker A, upload een bestand, kopieer het pad. Log in als gebruiker B in een andere browser, probeer het bestand op te halen via de REST API. Het antwoord moet 403 of 404 zijn, nooit 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]);

Uploadvalidatie

Valideer elke upload server-side, zelfs als de bucket MIME- en groottebeperkingen heeft. AI-codeertools genereren standaard alleen-client-validatie; dat beschermt niets.

  1. Hercheck het MIME-type server-side vanuit de daadwerkelijke bytes van het bestand, niet de Content-Type-header. Gebruik een bibliotheek zoals file-type (Node) of magic-byte sniffing. Een aanvaller kan Content-Type: image/jpeg claimen op een bestand dat eigenlijk een polyglot HTML / JavaScript-payload is.
  2. Verwijder EXIF-metadata uit geüploade afbeeldingen. EXIF kan GPS-coördinaten, apparaat-serienummers en tijdstempels bevatten. Gebruik sharp met .withMetadata(false) of exif-parser om te verwijderen vóór opslag.
  3. Weiger SVG's die script-tags of onload-handlers bevatten. SVG is XML — en veel AI-gegenereerde apps staan SVG-uploads toe als "gewoon een afbeelding". Gebruik DOMPurify server-side of weiger SVG-uploads volledig.
  4. Gebruik deterministische, onraadbare bestandsnamen. Bewaar de oorspronkelijke bestandsnaam niet. Gebruik een UUID of een hash van de bestandsinhoud. Originele bestandsnamen lekken informatie ("passport_scan_2024_01_15.jpg") en voorspelbare namen maken enumeratie mogelijk.

Getekende URL's

Getekende URL's zijn hoe clients toegang krijgen tot privé buckets. De vervaltijd, de bucket-scope en wat wordt gelogd doen ertoe.

  1. Standaard getekende URL-vervaltijd op 1 uur of minder. De createSignedUrl(path, expiresIn) van de Supabase JS-SDK neemt seconden. Gebruik nooit waarden zoals 31536000 (een jaar) — de URL wordt een permanente semi-openbare link.
  2. Sla getekende URL's nooit op in je database. Genereer verse server-side bij elke aanvraag. Een opgeslagen getekende URL met een 1-jaar-vervaltijd die lekt via een database-dump verleent langetermijntoegang.
  3. Log de generatie van getekende URL's, niet alleen bestandsuploads. Als je later een compromittering vermoedt, moet je weten wie welke URL wanneer heeft gegenereerd. Log auth.uid() + bucket + objectpad + tijdstempel.
  4. Gebruik de downloadAs-optie bij het serveren van door gebruikers geüploade bestanden. createSignedUrl(path, expiresIn, { download: '.jpg' }) dwingt een Content-Disposition: attachment-header af, zodat het bestand downloadt in plaats van rendert — verslaat de HTML / SVG / HTML-in-PDF-uitvoeringsklasse.

Operationele hygiëne

Opslagconfiguratie drift in de loop van de tijd. Deze vier operationele items houden het oppervlak strak.

  1. Audit buckets per kwartaal. Dashboard → Storage → Buckets. Bevestig dat openbare/private staat en MIME-typelijsten overeenkomen met wat de app verwacht. Buckets die "tijdelijk" zijn aangemaakt, worden permanent als niemand ze verwijdert.
  2. Monitor anonieme list-operaties. Opslaglogs (Dashboard → Logs → Storage) registreren LIST-aanvragen. Een piek in anonieme list-aanvragen tegen een privé bucket betekent dat iemand het van buitenaf aan het onderzoeken is.
  3. Stel een bewaarbeleid in voor kortstondige uploads. Tijdelijke buckets (afbeeldingsvoorbeeld, conceptuploads) moeten automatisch verwijderd worden na 24-72 uur via een geplande functie. Onbepaalde bewaring is een aansprakelijkheid onder GDPR / CCPA-data-minimalisatieverplichtingen.
  4. Voer maandelijks een FixVibe-scan uit. De baas.supabase-storage-public-check sondeert naar buckets die reageren op anonieme GET + LIST. Nieuwe buckets worden toegevoegd; oude veranderen van zichtbaarheid — alleen continue scanning vangt de drift.

Volgende stappen

Voer een FixVibe-scan uit tegen je productie-URL — anonieme opslaglijsten verschijnen onder baas.supabase-storage-public. Combineer deze checklist met Supabase RLS-scanner voor de tabellaag en Supabase service-rolsleutel blootgesteld in JavaScript voor de sleutel-blootstelling-aangrenzendheid. Voor opslagmisconfiguraties bij andere BaaS-providers, zie BaaS-misconfiguratiescanner.

// scan je baas-oppervlak

Vind de open tabel voordat iemand anders dat doet.

Voer een productie-URL in. FixVibe somt de BaaS-providers op waarmee je app communiceert, fingerprint hun openbare endpoints en rapporteert wat een niet-geauthenticeerde client kan lezen of schrijven. Gratis, geen installatie, geen kaart.

  • Gratis tier — 3 scans / maand, geen kaart bij aanmelden.
  • Passieve BaaS-fingerprinting — geen domeinverificatie nodig.
  • Supabase, Firebase, Clerk, Auth0, Appwrite en meer.
  • AI-fixprompts bij elke bevinding — plak terug in Cursor / Claude Code.
Voer een gratis BaaS-scan uit

geen aanmelding vereist

Supabase opslagbucket-beveiligingschecklist: 22 punten — Docs · FixVibe