// docs / baas security / supabase storage
Sjekkliste for Supabase storage-bucket-sikkerhet: 22 punkter
Supabase Storage er et tynt lag rundt en S3-kompatibel bucket pluss samme Row-Level Security-modell som databasen. Det betyr at de samme RLS-fallgruvene som påvirker tabeller, påvirker filtilgang — og noen storage-spesifikke som dukker opp når AI-kodeverktøy kobler opp opplastinger. Denne sjekklisten er 22 punkter på tvers av fem seksjoner: bucket-konfigurasjon, RLS-policyer, opplastingsvalidering, signerte URL-er og operativ hygiene. Hvert er verifiserbart på under 15 minutter.
Hvert punkt nedenfor er essensielt. For den underliggende RLS-mekanikken, se Supabase RLS-skanner. For nøkkel-eksponeringsklassen som ligger tett opp til lagring, se Supabase service-role-nøkkel eksponert i JavaScript.
Bucket-konfigurasjon
Start med de riktige standardene. En feilkonfigurert bucket lekker filer enten RLS-en din er korrekt eller ikke.
- La hver bucket være privat som standard. I Supabase Dashboard → Storage → Buckets, sett Public bucket-bryteren til av med mindre du har en eksplisitt grunn (markedsføringsressurser, offentlige avatarer uten PII). Offentlige buckets omgår RLS for leseoperasjoner — hvem som helst med bucket-navnet kan liste og laste ned.
- Sett en hard filstørrelsesgrense på hver bucket. Dashboard → Bucket-innstillinger → File size limit. 50 MB er en fornuftig standard for brukeropplastinger; hev den bevisst for video/store-fil-bruk. Uten en grense kan en enkelt skadelig opplasting tømme lagringskvoten din eller månedlig båndbredde.
- Begrens tillatte MIME-typer per bucket. Liste over tillatte MIME-typer — eksplisitt allowlist, ikke blocklist.
image/jpeg,image/png,image/webpfor bare-bilde-buckets. Tillat aldritext/html,application/javascriptellerimage/svg+xmli en bruker-innhold-bucket — de kjøres i nettleseren når de serveres via signert URL. - Bruk én bucket per innholdstype, ikke én delt bucket. Per-bucket-innstillinger (størrelse, MIME-typer, RLS-policyer) er granulariteten du har. En
user-avatars-bucket, endocument-uploads-bucket og enpublic-assets-bucket er enklere å låse ned enn én blandet bucket. - Verifiser CORS-konfigurasjon hvis frontend laster opp. Hvis brukere laster opp direkte fra nettleseren til en signert URL, må bucketens CORS liste produksjonsoriginen din.
*er kun akseptabelt for offentlige buckets — aldri for buckets som inneholder bruker-PII.
RLS-policyer på storage.objects
Supabase Storage lagrer filmetadata i storage.objects-tabellen. RLS på den tabellen kontrollerer hvem som kan lese, laste opp, oppdatere eller slette filer. Uten RLS er bucketens public/private-flagg din eneste beskyttelse.
- Bekreft at RLS er aktivert på storage.objects.
SELECT rowsecurity FROM pg_tables WHERE schemaname = 'storage' AND tablename = 'objects';må returneretrue. Supabase aktiverer det som standard på nye prosjekter; verifiser at det ikke er deaktivert. - Skriv en SELECT-policy avgrenset til
auth.uid()for private buckets.CREATE POLICY "users_read_own_files" ON storage.objects FOR SELECT USING (auth.uid()::text = (storage.foldername(name))[1]);. Konvensjonen er å lagre filer under[user-id]/[filename]og brukestorage.foldername()for å hente ut eieren fra stien. - Skriv en INSERT-policy som håndhever samme stikonvensjon.
CREATE POLICY "users_upload_own" ON storage.objects FOR INSERT WITH CHECK (auth.uid()::text = (storage.foldername(name))[1]);. Uten WITH CHECK kan en autentisert bruker laste opp i en annen brukers mappe. - Legg til UPDATE- og DELETE-policyer hvis appen din støtter filredigering eller -sletting. Hver kommando trenger sin egen policy. Å hoppe over DELETE betyr at autentiserte brukere ikke kan fjerne sine egne filer; å hoppe over UPDATE betyr at filoverskrivinger feiler lydløst.
- Test tilgang på tvers av brukere i to nettleserøkter. Logg inn som Bruker A, last opp en fil, kopier stien. Logg inn som Bruker B i en annen nettleser, prøv å hente filen via REST-API-et. Svaret må være
403eller404, aldri200.
-- 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]);Opplastingsvalidering
Valider hver opplasting serverside, selv når bucketen har MIME- og størrelsesbegrensninger. AI-kodeverktøy genererer kun klientvalidering som standard; det beskytter ingenting.
- Sjekk MIME-typen serverside på nytt fra filens faktiske bytes, ikke
Content-Type-headeren. Bruk et bibliotek somfile-type(Node) eller magic-byte-sniffing. En angriper kan oppgiContent-Type: image/jpegpå en fil som faktisk er en polyglot HTML/JavaScript-payload. - Strip EXIF-metadata fra opplastede bilder. EXIF kan inneholde GPS-koordinater, enhetens serienumre og tidsstempler. Bruk
sharpmed.withMetadata(false)ellerexif-parserfor å strippe før lagring. - Avvis SVG-er som inneholder
script-tagger elleronload-handlere. SVG er XML — og mange AI-genererte apper tillater SVG-opplastinger som "bare et bilde." BrukDOMPurifyserverside eller avvis SVG-opplastinger fullstendig. - Bruk deterministiske, ugjettbare filnavn. Ikke behold det opprinnelige filnavnet. Bruk en UUID eller en hash av filinnholdet. Opprinnelige filnavn lekker ("
passport_scan_2024_01_15.jpg") og forutsigbare navn muliggjør enumerering.
Signerte URL-er
Signerte URL-er er måten klienter får tilgang til private buckets. Utløpstiden, bucket-avgrensningen og hva som logges, betyr noe.
- La signert-URL-utløp som standard være 1 time eller mindre. Supabase JS-SDK-ens
createSignedUrl(path, expiresIn)tar sekunder. Bruk aldri verdier som31536000(ett år) — URL-en blir en permanent semi-offentlig lenke. - Lagre aldri signerte URL-er i databasen din. Generer ferske serverside ved hver request. En lagret signert URL med 1-års utløp som lekker via en databasedump, gir langsiktig tilgang.
- Logg generering av signerte URL-er, ikke bare filopplastinger. Hvis du mistenker en kompromittering senere, må du vite hvem som genererte hvilken URL når. Logg
auth.uid()+ bucket + objektsti + tidsstempel. - Bruk
downloadAs-opsjonen når du serverer brukeropplastede filer.createSignedUrl(path, expiresIn, { download: '.jpg' })tvinger fram enContent-Disposition: attachment-header slik at filen lastes ned i stedet for å rendres — slår ut HTML/SVG/HTML-i-PDF-utførelsesklassen.
Operativ hygiene
Storage-konfigurasjon drifter over tid. Disse fire operative punktene holder flaten stram.
- Gransk buckets kvartalsvis. Dashboard → Storage → Buckets. Bekreft at public/private-status og MIME-type-lister samsvarer med det appen forventer. Buckets opprettet "midlertidig" blir permanente hvis ingen fjerner dem.
- Overvåk anonyme listoperasjoner. Storage-logger (Dashboard → Logs → Storage) registrerer
LIST-requests. En topp av anonyme list-requests mot en privat bucket betyr at noen sonderer den utenfra. - Sett en retensjonspolicy for efemere opplastinger. Temp-buckets (forhåndsvisning, utkastopplastinger) bør auto-slettes etter 24–72 timer via en planlagt funksjon. Ubegrenset retensjon er en forpliktelse under GDPR/CCPA-dataminimerings-krav.
- Kjør en FixVibe-skanning månedlig.
baas.supabase-storage-public-sjekken sonderer etter buckets som svarer på anonymeGET+LIST. Nye buckets dukker opp; gamle endrer synlighet — kun kontinuerlig skanning fanger driften.
Neste steg
Kjør en FixVibe-skanning mot produksjons-URL-en din — anonyme storage-listinger dukker opp under baas.supabase-storage-public. Kombiner denne sjekklisten med Supabase RLS-skanner for tabell-laget og Supabase service-role-nøkkel eksponert i JavaScript for den tilstøtende nøkkel-eksponeringen. For storage-feilkonfigurasjoner hos andre BaaS-leverandører, se BaaS-feilkonfigurasjonsskanner.
