// 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.
- 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.
- 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.
- Beperk toegestane MIME-types per bucket. Lijst van toegestane MIME-types — expliciete allowlist, geen blocklist.
image/jpeg,image/png,image/webpvoor alleen-afbeeldingen-buckets. Sta nooittext/html,application/javascriptofimage/svg+xmltoe in een gebruikersinhoud-bucket — ze voeren uit in de browser wanneer ze via getekende URL worden geserveerd. - 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, eendocument-uploads-bucket en eenpublic-assets-bucket zijn gemakkelijker te vergrendelen dan één gemengde bucket. - 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.
- Bevestig dat RLS is ingeschakeld op storage.objects.
SELECT rowsecurity FROM pg_tables WHERE schemaname = 'storage' AND tablename = 'objects';moettrueretourneren. Supabase schakelt het standaard in op nieuwe projecten; verifieer dat het niet is uitgeschakeld. - 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]enstorage.foldername()te gebruiken om de eigenaar uit het pad te extraheren. - 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. - 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.
- 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
403of404zijn, nooit200.
-- 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.
- Hercheck het MIME-type server-side vanuit de daadwerkelijke bytes van het bestand, niet de
Content-Type-header. Gebruik een bibliotheek zoalsfile-type(Node) of magic-byte sniffing. Een aanvaller kanContent-Type: image/jpegclaimen op een bestand dat eigenlijk een polyglot HTML / JavaScript-payload is. - Verwijder EXIF-metadata uit geüploade afbeeldingen. EXIF kan GPS-coördinaten, apparaat-serienummers en tijdstempels bevatten. Gebruik
sharpmet.withMetadata(false)ofexif-parserom te verwijderen vóór opslag. - Weiger SVG's die
script-tags ofonload-handlers bevatten. SVG is XML — en veel AI-gegenereerde apps staan SVG-uploads toe als "gewoon een afbeelding". GebruikDOMPurifyserver-side of weiger SVG-uploads volledig. - 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.
- Standaard getekende URL-vervaltijd op 1 uur of minder. De
createSignedUrl(path, expiresIn)van de Supabase JS-SDK neemt seconden. Gebruik nooit waarden zoals31536000(een jaar) — de URL wordt een permanente semi-openbare link. - 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.
- 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. - Gebruik de
downloadAs-optie bij het serveren van door gebruikers geüploade bestanden.createSignedUrl(path, expiresIn, { download: '.jpg' })dwingt eenContent-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.
- 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.
- 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. - 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.
- Voer maandelijks een FixVibe-scan uit. De
baas.supabase-storage-public-check sondeert naar buckets die reageren op anoniemeGET+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.
