// docs / baas security / supabase storage
Lista de verificare pentru securitatea bucket-urilor de stocare Supabase: 22 de elemente
Supabase Storage este un wrapper subțire în jurul unui bucket compatibil S3 plus același model Row-Level Security ca baza de date. Asta înseamnă că aceleași capcane RLS care afectează tabelele afectează accesul la fișiere — plus câteva specifice de stocare care apar atunci când instrumentele de codare AI configurează încărcările. Această listă de verificare are 22 de elemente în cinci secțiuni: configurarea bucket-ului, politicile RLS, validarea încărcărilor, URL-uri semnate și igienă operațională. Fiecare este verificabilă în mai puțin de 15 minute.
Fiecare element de mai jos este esențial. Pentru mecanica RLS de bază, vezi Scaner RLS Supabase. Pentru clasa de expunere a cheii adiacentă stocării, vezi Cheia service role Supabase expusă în JavaScript.
Configurarea bucket-ului
Începe cu setările implicite corecte. Un bucket configurat greșit scurge fișiere indiferent dacă RLS-ul tău este corect sau nu.
- Setează implicit fiecare bucket pe privat. În Supabase Dashboard → Storage → Buckets, setează comutatorul Public bucket pe off, cu excepția cazului în care ai un motiv explicit (resurse de marketing, avatare publice fără PII). Bucket-urile publice ocolesc RLS pentru operațiunile de citire — oricine cu numele bucket-ului poate lista și descărca.
- Setează o limită fermă de dimensiune a fișierului pe fiecare bucket. Dashboard → Bucket settings → File size limit. 50 MB este o valoare implicită rezonabilă pentru încărcările utilizatorilor; ridică-o deliberat pentru cazuri de utilizare video / fișiere mari. Fără o limită, o singură încărcare malițioasă poate epuiza cota de stocare sau lățimea de bandă lunară.
- Restricționează tipurile MIME permise pe bucket. Listă de tipuri MIME permise — allowlist explicită, nu blocklist.
image/jpeg,image/png,image/webppentru bucket-uri numai-imagine. Niciodată să nu permițitext/html,application/javascriptsauimage/svg+xmlîntr-un bucket de conținut al utilizatorilor — se execută în browser când sunt servite prin URL semnat. - Folosește un bucket per tip de conținut, nu un bucket partajat. Setările per bucket (dimensiune, tipuri MIME, politici RLS) sunt granularitatea pe care o ai. Un bucket
user-avatars, un bucketdocument-uploadsși un bucketpublic-assetssunt mai ușor de blocat decât un bucket mixt. - Verifică configurația CORS dacă se face upload din frontend. Dacă utilizatorii încarcă direct din browser către un URL semnat, CORS-ul bucket-ului trebuie să listeze originea de producție.
*este acceptabil doar pentru bucket-uri publice — niciodată pentru bucket-uri care conțin PII.
Politici RLS pe storage.objects
Supabase Storage stochează metadatele fișierelor în tabela storage.objects. RLS pe acea tabelă controlează cine poate citi, încărca, actualiza sau șterge fișiere. Fără RLS, flag-ul public/privat al bucket-ului este singura protecție.
- Confirmă că RLS este activat pe storage.objects.
SELECT rowsecurity FROM pg_tables WHERE schemaname = 'storage' AND tablename = 'objects';trebuie să returnezetrue. Supabase îl activează implicit pe proiectele noi; verifică să nu fi fost dezactivat. - Scrie o politică SELECT delimitată la
auth.uid()pentru bucket-urile private.CREATE POLICY "users_read_own_files" ON storage.objects FOR SELECT USING (auth.uid()::text = (storage.foldername(name))[1]);. Convenția este să stochezi fișierele sub[user-id]/[filename]și să foloseștistorage.foldername()pentru a extrage proprietarul din cale. - Scrie o politică INSERT care impune aceeași convenție de cale.
CREATE POLICY "users_upload_own" ON storage.objects FOR INSERT WITH CHECK (auth.uid()::text = (storage.foldername(name))[1]);. Fără WITH CHECK, un utilizator autentificat poate încărca în folder-ul altui utilizator. - Adaugă politici UPDATE și DELETE dacă aplicația ta acceptă editări sau ștergeri de fișiere. Fiecare comandă are nevoie de propria politică. Omiterea DELETE înseamnă că utilizatorii autentificați nu-și pot șterge propriile fișiere; omiterea UPDATE înseamnă că suprascrierile de fișiere eșuează în tăcere.
- Testează accesul între utilizatori în două sesiuni de browser. Conectează-te ca Utilizator A, încarcă un fișier, copiază calea. Conectează-te ca Utilizator B în alt browser, încearcă să descarci fișierul prin API-ul REST. Răspunsul trebuie să fie
403sau404, niciodată200.
-- 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]);Validarea încărcărilor
Validează fiecare încărcare pe server, chiar și când bucket-ul are constrângeri MIME și de dimensiune. Instrumentele de codare AI generează implicit doar validare pe client; asta nu protejează nimic.
- Reverifică tipul MIME pe server din biții reali ai fișierului, nu din antetul
Content-Type. Folosește o bibliotecă precumfile-type(Node) sau magic-byte sniffing. Un atacator poate pretindeContent-Type: image/jpegpe un fișier care este de fapt un payload polyglot HTML / JavaScript. - Elimină metadatele EXIF din imaginile încărcate. EXIF poate conține coordonate GPS, numere de serie ale dispozitivelor și marcaje temporale. Folosește
sharpcu.withMetadata(false)sauexif-parserpentru a le elimina înainte de stocare. - Respinge SVG-urile care conțin tag-uri
scriptsau handler-eonload. SVG este XML — și multe aplicații generate de AI permit încărcări SVG ca „doar o imagine". FoloseșteDOMPurifype server sau refuză complet încărcările SVG. - Folosește nume de fișiere deterministe, neghicibile. Nu păstra numele original al fișierului. Folosește un UUID sau un hash al conținutului fișierului. Numele originale ale fișierelor scurg informații („
passport_scan_2024_01_15.jpg"), iar numele previzibile permit enumerarea.
URL-uri semnate
URL-urile semnate sunt modul în care clienții accesează bucket-urile private. Expirarea, domeniul bucket-ului și ce se loghează contează.
- Setează implicit expirarea URL-urilor semnate la 1 oră sau mai puțin. Funcția
createSignedUrl(path, expiresIn)din SDK-ul JS Supabase ia secunde. Niciodată să nu folosești valori precum31536000(un an) — URL-ul devine un link semi-public permanent. - Niciodată să nu stochezi URL-uri semnate în baza ta de date. Generează altele proaspete pe server la fiecare cerere. Un URL semnat stocat cu o expirare de 1 an care scurge printr-un dump al bazei de date acordă acces pe termen lung.
- Loghează generarea URL-urilor semnate, nu doar încărcările de fișiere. Dacă suspectezi mai târziu o compromitere, trebuie să știi cine a generat care URL și când. Loghează
auth.uid()+ bucket + calea obiectului + marcaj temporal. - Folosește opțiunea
downloadAscând servești fișiere încărcate de utilizatori.createSignedUrl(path, expiresIn, { download: '.jpg' })forțează un antetContent-Disposition: attachment, astfel încât fișierul se descarcă în loc să se randeze — învinge clasa de execuție HTML / SVG / HTML-în-PDF.
Igienă operațională
Configurația de stocare se deformează în timp. Aceste patru elemente operaționale păstrează suprafața strânsă.
- Auditează bucket-urile trimestrial. Dashboard → Storage → Buckets. Confirmă starea public/privat și listele de tipuri MIME se potrivesc cu ceea ce așteaptă aplicația. Bucket-urile create „temporar" devin permanente dacă nimeni nu le elimină.
- Monitorizează operațiunile de listare anonime. Log-urile Storage (Dashboard → Logs → Storage) înregistrează cererile
LIST. O creștere bruscă de cereri de listare anonime împotriva unui bucket privat înseamnă că cineva îl sondează din exterior. - Setează o politică de retenție pentru încărcările efemere. Bucket-urile temporare (previzualizare imagini, încărcări draft) ar trebui să se autoșteargă după 24-72 de ore printr-o funcție programată. Retenția nedefinită este o răspundere conform obligațiilor de minimizare a datelor GDPR / CCPA.
- Rulează o scanare FixVibe lunar. Verificarea
baas.supabase-storage-publicsondează bucket-uri care răspund laGET+LISTanonim. Bucket-uri noi sunt adăugate; cele vechi își schimbă vizibilitatea — doar scanarea continuă detectează această deformare.
Pași următori
Rulează o scanare FixVibe împotriva URL-ului tău de producție — listările de stocare anonime apar sub baas.supabase-storage-public. Combină această listă de verificare cu Scaner RLS Supabase pentru stratul de tabele și Cheia service role Supabase expusă în JavaScript pentru adiacența expunerii cheii. Pentru configurări greșite de stocare la alți furnizori BaaS, vezi Scaner de configurări greșite BaaS.
