// 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. - Scrivi una policy INSERT che applichi la stessa convenzione di percorso.
CREATE POLICY "users_upload_own" ON storage.objects FOR INSERT WITH CHECK (auth.uid()::text = (storage.foldername(name))[1]);. Senza WITH CHECK, un utente autenticato può caricare nella cartella di un altro utente. - 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.
- Esegui una scansione FixVibe mensilmente. Il check
baas.supabase-storage-publicsonda i bucket che rispondono aGET+LISTanonimi. Nuovi bucket vengono aggiunti; i vecchi cambiano visibilità — solo la scansione continua cattura la deriva.
Prossimi passi
Esegui una scansione FixVibe contro la tua URL di produzione — gli elenchi anonimi dello storage compaiono sotto baas.supabase-storage-public. Abbina questa checklist a Scanner RLS Supabase per lo strato tabelle e Chiave di service-role Supabase esposta in JavaScript per l'adiacenza esposizione-chiavi. Per configurazioni errate dello storage su altri provider BaaS, vedi Scanner di configurazioni errate BaaS.
