// docs / baas security / supabase storage
Checklist di sicurezza dei bucket Supabase Storage: 22 elementi
Supabase Storage è un sottile wrapper attorno a un bucket S3-compatibile più lo stesso modello di Row-Level Security del database. Questo significa che le stesse insidie RLS che affliggono le tabelle affliggono l'accesso ai file — più alcune specifiche dello storage che appaiono quando gli strumenti di codifica IA cablano gli upload. Questa checklist conta 22 elementi in cinque sezioni: configurazione del bucket, policy RLS, validazione upload, URL firmate e igiene operativa. Ciascuno è verificabile in meno di 15 minuti.
Ogni elemento sotto è essenziale. Per i meccanismi RLS sottostanti, vedi Scanner RLS Supabase. Per la classe di esposizione di chiavi adiacente allo storage, vedi Chiave di service-role Supabase esposta in JavaScript.
Configurazione del bucket
Inizia con i default giusti. Un bucket mal configurato perde file sia che il tuo RLS sia corretto o no.
- Imposta ogni bucket come privato di default. Nella dashboard Supabase → Storage → Buckets, disattiva il toggle Public bucket a meno che tu non abbia una ragione esplicita (asset di marketing, avatar pubblici senza PII). I bucket pubblici bypassano RLS per le operazioni di lettura — chiunque conosca il nome del bucket può elencare e scaricare.
- Imposta un limite duro di dimensione file su ogni bucket. Dashboard → Impostazioni del bucket → Limite dimensione file. 50 MB è un default sensato per gli upload utente; aumentalo deliberatamente per casi d'uso video / file grandi. Senza un limite, un singolo upload malevolo può esaurire la tua quota di storage o la tua banda mensile.
- Limita i tipi MIME permessi per bucket. Lista tipi MIME permessi — allowlist esplicita, non blocklist.
image/jpeg,image/png,image/webpper bucket solo-immagini. Non permettere maitext/html,application/javascriptoimage/svg+xmlin un bucket di contenuto utente — vengono eseguiti nel browser quando serviti via URL firmata. - Usa un bucket per tipo di contenuto, non un bucket condiviso. Le impostazioni per-bucket (dimensione, tipi MIME, policy RLS) sono la granularità che hai. Un bucket
user-avatars, un bucketdocument-uploadse un bucketpublic-assetssono più facili da bloccare di un bucket misto. - Verifica la configurazione CORS se ci sono upload dal frontend. Se gli utenti caricano direttamente dal browser a una URL firmata, il CORS del bucket deve elencare la tua origine di produzione.
*è accettabile solo per bucket pubblici — mai per bucket contenenti PII utente.
Policy RLS su storage.objects
Supabase Storage memorizza i metadati dei file nella tabella storage.objects. RLS su quella tabella controlla chi può leggere, caricare, aggiornare o cancellare file. Senza RLS, il flag pubblico/privato del bucket è la tua unica protezione.
- Conferma che RLS è abilitato su storage.objects.
SELECT rowsecurity FROM pg_tables WHERE schemaname = 'storage' AND tablename = 'objects';deve restituiretrue. Supabase lo abilita di default su nuovi progetti; verifica che non sia stato disabilitato. - Scrivi una policy SELECT limitata ad
auth.uid()per bucket privati.CREATE POLICY "users_read_own_files" ON storage.objects FOR SELECT USING (auth.uid()::text = (storage.foldername(name))[1]);. La convenzione è memorizzare i file sotto[user-id]/[filename]e usarestorage.foldername()per estrarre il proprietario dal percorso. - Scope permitted INSERT operations to the user’s folder.
CREATE POLICY "users_upload_own" ON storage.objects FOR INSERT WITH CHECK (auth.uid()::text = (storage.foldername(name))[1]);. With RLS enabled, no applicable INSERT policy means denial; an overly broad applicable policy can permit uploads outside the intended folder. Review all applicable policies and the bucket constraint. - Aggiungi policy UPDATE e DELETE se la tua app supporta modifiche o cancellazioni di file. Ogni comando ha bisogno della sua policy. Saltare DELETE significa che gli utenti autenticati non possono rimuovere i propri file; saltare UPDATE significa che le sovrascritture di file falliscono silenziosamente.
- Testa l'accesso cross-utente in due sessioni del browser. Accedi come Utente A, carica un file, copia il percorso. Accedi come Utente B in un altro browser, prova a recuperare il file via API REST. La risposta deve essere
403o404, mai200.
-- 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]);Validazione degli upload
Valida ogni upload lato server, anche quando il bucket ha vincoli MIME e di dimensione. Gli strumenti di codifica IA generano validazione solo lato client per default; quella non protegge nulla.
- Ricontrolla il tipo MIME lato server dai byte reali del file, non dall'header
Content-Type. Usa una libreria comefile-type(Node) o sniffing dei magic bytes. Un attaccante può dichiarareContent-Type: image/jpegsu un file che è in realtà un payload poliglotta HTML / JavaScript. - Rimuovi i metadati EXIF dalle immagini caricate. EXIF può contenere coordinate GPS, numeri seriali di dispositivi e timestamp. Usa
sharpcon.withMetadata(false)oexif-parserper ripulire prima dell'archiviazione. - Rifiuta SVG che contengono tag
scripto handleronload. SVG è XML — e molte app generate da IA permettono upload SVG come "solo un'immagine". UsaDOMPurifylato server o rifiuta upload SVG del tutto. - Usa nomi di file deterministici e non indovinabili. Non preservare il nome originale. Usa un UUID o un hash del contenuto del file. I nomi originali fanno trapelare informazioni ("
passport_scan_2024_01_15.jpg") e i nomi prevedibili permettono l'enumerazione.
URL firmate
Le URL firmate sono il modo in cui i client accedono ai bucket privati. La scadenza, lo scope del bucket e cosa viene loggato sono importanti.
- Imposta la scadenza di default delle URL firmate a 1 ora o meno. La funzione
createSignedUrl(path, expiresIn)dell'SDK JS Supabase prende secondi. Non usare mai valori come31536000(un anno) — l'URL diventa un link semi-pubblico permanente. - Non memorizzare mai URL firmate nel tuo database. Generane di fresche lato server a ogni richiesta. Un'URL firmata memorizzata con scadenza di 1 anno che trapela via dump del database concede accesso a lungo termine.
- Logga la generazione delle URL firmate, non solo gli upload dei file. Se sospetti una compromissione più tardi, devi sapere chi ha generato quale URL e quando. Logga
auth.uid()+ bucket + percorso oggetto + timestamp. - Usa l'opzione
downloadAsquando servi file caricati dagli utenti.createSignedUrl(path, expiresIn, { download: '.jpg' })forza un headerContent-Disposition: attachmentcosì il file viene scaricato invece di renderizzato — sconfigge la classe di esecuzione HTML / SVG / HTML-in-PDF.
Igiene operativa
La configurazione dello storage va alla deriva nel tempo. Questi quattro elementi operativi mantengono la superficie stretta.
- Audita i bucket trimestralmente. Dashboard → Storage → Buckets. Conferma che lo stato pubblico/privato e le liste di tipi MIME corrispondono a ciò che l'app si aspetta. I bucket creati "temporaneamente" diventano permanenti se nessuno li rimuove.
- Monitora le operazioni di elenco anonime. I log dello storage (Dashboard → Logs → Storage) registrano le richieste
LIST. Un picco di richieste di elenco anonime contro un bucket privato significa che qualcuno lo sta sondando dall'esterno. - Imposta una policy di retention per upload effimeri. I bucket temporanei (anteprima immagini, draft di upload) dovrebbero auto-cancellarsi dopo 24-72 ore via funzione schedulata. La retention indefinita è una responsabilità sotto gli obblighi di minimizzazione dei dati GDPR / CCPA.
- Run a FixVibe scan monthly. FixVibe reports storage buckets that anonymous visitors can list or read. New buckets get added; old ones change visibility — only continuous scanning catches the drift.
Prossimi passi
Run a FixVibe scan against your production URL — anonymously listable storage shows up as its own finding. Pair this checklist with Supabase RLS scanner for the table layer and Supabase service role key exposed in JavaScript for the key-exposure adjacency. For storage misconfigurations across other BaaS providers, see BaaS misconfiguration scanner.
