FixVibe

// 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.

  1. 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.
  2. 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.
  3. Limita i tipi MIME permessi per bucket. Lista tipi MIME permessi — allowlist esplicita, non blocklist. image/jpeg, image/png, image/webp per bucket solo-immagini. Non permettere mai text/html, application/javascript o image/svg+xml in un bucket di contenuto utente — vengono eseguiti nel browser quando serviti via URL firmata.
  4. 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 bucket document-uploads e un bucket public-assets sono più facili da bloccare di un bucket misto.
  5. 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.

  1. Conferma che RLS è abilitato su storage.objects. SELECT rowsecurity FROM pg_tables WHERE schemaname = 'storage' AND tablename = 'objects'; deve restituire true. Supabase lo abilita di default su nuovi progetti; verifica che non sia stato disabilitato.
  2. 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 usare storage.foldername() per estrarre il proprietario dal percorso.
  3. 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.
  4. 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.
  5. 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 403 o 404, mai 200.
sql
-- 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.

  1. Ricontrolla il tipo MIME lato server dai byte reali del file, non dall'header Content-Type. Usa una libreria come file-type (Node) o sniffing dei magic bytes. Un attaccante può dichiarare Content-Type: image/jpeg su un file che è in realtà un payload poliglotta HTML / JavaScript.
  2. Rimuovi i metadati EXIF dalle immagini caricate. EXIF può contenere coordinate GPS, numeri seriali di dispositivi e timestamp. Usa sharp con .withMetadata(false) o exif-parser per ripulire prima dell'archiviazione.
  3. Rifiuta SVG che contengono tag script o handler onload. SVG è XML — e molte app generate da IA permettono upload SVG come "solo un'immagine". Usa DOMPurify lato server o rifiuta upload SVG del tutto.
  4. 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.

  1. 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 come 31536000 (un anno) — l'URL diventa un link semi-pubblico permanente.
  2. 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.
  3. 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.
  4. Usa l'opzione downloadAs quando servi file caricati dagli utenti. createSignedUrl(path, expiresIn, { download: '.jpg' }) forza un header Content-Disposition: attachment così 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.

  1. 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.
  2. 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.
  3. 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.
  4. Esegui una scansione FixVibe mensilmente. Il check baas.supabase-storage-public sonda i bucket che rispondono a GET + LIST anonimi. 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.

// scansiona la tua superficie baas

Trova la tabella aperta prima che lo faccia qualcun altro.

Inserisci una URL di produzione. FixVibe enumera i provider BaaS con cui parla la tua app, identifica i loro endpoint pubblici e riporta cosa un client non autenticato può leggere o scrivere. Gratis, senza installazione, senza carta.

  • Piano gratuito — 3 scansioni/mese, senza carta d'iscrizione.
  • Fingerprinting BaaS passivo — nessuna verifica del dominio necessaria.
  • Supabase, Firebase, Clerk, Auth0, Appwrite e altri.
  • Prompt di correzione IA su ogni risultato — incollali in Cursor / Claude Code.
Esegui scansione BaaS gratuita

nessuna registrazione richiesta

Checklist di sicurezza dei bucket Supabase Storage: 22 elementi — Docs · FixVibe