// docs / baas security / supabase storage
Supabase storage-bucket sikkerhedstjekliste: 22 punkter
Supabase Storage er en tynd indpakning omkring en S3-kompatibel bucket plus den samme Row-Level Security-model som databasen. Det betyder, at de samme RLS-faldgruber, der påvirker tabeller, påvirker filadgang — og nogle få storage-specifikke, der dukker op, når AI-kodeværktøjer kobler uploads. Denne tjekliste er 22 punkter på tværs af fem afsnit: bucket-konfiguration, RLS-politikker, upload-validering, signed URLs og operationel hygiejne. Hvert er verificerbart på under 15 minutter.
Hvert punkt nedenfor er essentielt. For den underliggende RLS-mekanik, se Supabase RLS-scanner. For den nøgleeksponeringsklasse, der støder op til storage, se Supabase service-role-nøgle eksponeret i JavaScript.
Bucket-konfiguration
Start med de rigtige standarder. En fejlkonfigureret bucket lækker filer, uanset om din RLS er korrekt eller ej.
- Sæt som standard alle buckets til private. I Supabase Dashboard → Storage → Buckets, sæt Public bucket-toggle til off, medmindre du har en eksplicit grund (marketingaktiver, offentlige avatarer uden PII). Offentlige buckets omgår RLS for læseoperationer — enhver med bucket-navnet kan liste og downloade.
- Sæt en hård filstørrelsesgrænse på hver bucket. Dashboard → Bucket settings → File size limit. 50 MB er en fornuftig standard for brugeruploads; hæv den bevidst for video / store filer. Uden en grænse kan en enkelt ondsindet upload udmatte din storage-kvote eller din månedlige båndbredde.
- Begræns tilladte MIME-typer per bucket. Listen over tilladte MIME-typer — eksplicit tilladelsesliste, ikke blokliste.
image/jpeg,image/png,image/webpfor billed-kun-buckets. Tillad aldrigtext/html,application/javascriptellerimage/svg+xmli en brugerindholdsbucket — de eksekveres i browseren, når de serveres via signed URL. - Brug én bucket per indholdstype, ikke én delt bucket. Per-bucket-indstillinger (størrelse, MIME-typer, RLS-politikker) er den granularitet, du har. En
user-avatars-bucket, endocument-uploads-bucket og enpublic-assets-bucket er nemmere at låse ned end én blandet bucket. - Verificer CORS-konfiguration, hvis frontend uploader. Hvis brugere uploader direkte fra browseren til en signed URL, skal bucket-CORS angive din produktionsoprindelse.
*er kun acceptabelt for offentlige buckets — aldrig for buckets, der indeholder bruger-PII.
RLS-politikker på storage.objects
Supabase Storage gemmer filmetadata i storage.objects-tabellen. RLS på den tabel styrer, hvem der kan læse, uploade, opdatere eller slette filer. Uden RLS er bucketens offentlige/private-flag din eneste beskyttelse.
- Bekræft, at RLS er aktiveret på storage.objects.
SELECT rowsecurity FROM pg_tables WHERE schemaname = 'storage' AND tablename = 'objects';skal returneretrue. Supabase aktiverer det som standard på nye projekter; verificer, at det ikke er deaktiveret. - Skriv en SELECT-politik begrænset 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]);. Konventionen er at gemme filer under[user-id]/[filename]og brugestorage.foldername()til at udtrække ejeren fra stien. - Skriv en INSERT-politik, der håndhæver den samme stikonvention.
CREATE POLICY "users_upload_own" ON storage.objects FOR INSERT WITH CHECK (auth.uid()::text = (storage.foldername(name))[1]);. Uden WITH CHECK kan en autentificeret bruger uploade i en anden brugers mappe. - Tilføj UPDATE- og DELETE-politikker, hvis din app understøtter filredigeringer eller -sletninger. Hvert kommando har brug for sin egen politik. At springe DELETE over betyder, at autentificerede brugere ikke kan fjerne deres egne filer; at springe UPDATE over betyder, at filoverskrivninger fejler lydløst.
- Test cross-user-adgang i to browsersessioner. Log ind som Bruger A, upload en fil, kopier stien. Log ind som Bruger B i en anden browser, prøv at hente filen via REST-API'en. Svaret skal være
403eller404, aldrig200.
-- 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-validering
Valider hver upload server-side, selv når bucketen har MIME- og størrelsesbegrænsninger. AI-kodeværktøjer genererer client-only-validering som standard; det beskytter intet.
- Tjek MIME-type igen server-side fra filens faktiske bytes, ikke
Content-Type-headeren. Brug et bibliotek somfile-type(Node) eller magic-byte-sniffing. En angriber kan hævdeContent-Type: image/jpegpå en fil, der faktisk er en polyglot HTML / JavaScript-payload. - Fjern EXIF-metadata fra uploadede billeder. EXIF kan indeholde GPS-koordinater, enhedsserienumre og tidsstempler. Brug
sharpmed.withMetadata(false)ellerexif-parserfor at fjerne det før storage. - Afvis SVG'er, der indeholder
script-tags elleronload-handlers. SVG er XML — og mange AI-genererede apps tillader SVG-uploads som "bare et billede." BrugDOMPurifyserver-side eller afvis SVG-uploads helt. - Brug deterministiske, ugættelige filnavne. Bevar ikke det originale filnavn. Brug et UUID eller et hash af filindholdet. Originale filnavne lækker ("
passport_scan_2024_01_15.jpg"), og forudsigelige navne muliggør optælling.
Signed URLs
Signed URLs er, hvordan klienter tilgår private buckets. Udløb, bucket-omfang og hvad der logges betyder noget.
- Sæt signed-URL-udløb til 1 time eller mindre som standard. Supabase JS SDK's
createSignedUrl(path, expiresIn)tager sekunder. Brug aldrig værdier som31536000(et år) — URL'en bliver et permanent semi-offentligt link. - Gem aldrig signed URLs i din database. Generer friske server-side på hver anmodning. En gemt signed URL med 1-års udløb, der lækker via en database-dump, giver langvarig adgang.
- Log signed-URL-generering, ikke kun filuploads. Hvis du mistænker en kompromittering senere, har du brug for at vide, hvem der genererede hvilken URL hvornår. Log
auth.uid()+ bucket + objektsti + tidsstempel. - Brug
downloadAs-optionen, når du serverer brugeruploadede filer.createSignedUrl(path, expiresIn, { download: '.jpg' })tvinger enContent-Disposition: attachment-header, så filen downloades i stedet for at blive renderet — slår HTML / SVG / HTML-i-PDF-eksekveringsklassen.
Operationel hygiejne
Storage-konfiguration drifter over tid. Disse fire operationelle punkter holder overfladen stram.
- Revider buckets kvartalsvis. Dashboard → Storage → Buckets. Bekræft offentlig/privat tilstand og MIME-typelister matcher, hvad appen forventer. Buckets oprettet "midlertidigt" bliver permanente, hvis ingen fjerner dem.
- Overvåg anonyme listeoperationer. Storage-logfiler (Dashboard → Logs → Storage) registrerer
LIST-anmodninger. En stigning i anonyme listeanmodninger mod en privat bucket betyder, at nogen sonderer den udefra. - Sæt en opbevaringspolitik for kortlivede uploads. Temp-buckets (billedforhåndsvisning, kladdeuploads) skal automatisk slettes efter 24-72 timer via en planlagt funktion. Ubegrænset opbevaring er en byrde under GDPR / CCPA-dataminimeringsforpligtelser.
- Kør en FixVibe-scanning månedligt.
baas.supabase-storage-public-checken sonderer for buckets, der svarer på anonymGET+LIST. Nye buckets tilføjes; gamle ændrer synlighed — kun kontinuerlig scanning fanger driften.
Næste skridt
Kør en FixVibe-scanning mod din produktions-URL — anonyme storage-listninger dukker op under baas.supabase-storage-public. Par denne tjekliste med Supabase RLS-scanner for tabellaget og Supabase service-role-nøgle eksponeret i JavaScript for nøgleeksponeringsnaboskabet. For storage-fejlkonfigurationer på tværs af andre BaaS-udbydere, se BaaS-fejlkonfigurationsscanner.
