FixVibe

// docs / baas security / supabase storage

Lista kontrolna bezpieczeństwa kosza magazynu Supabase: 22 punkty

Supabase Storage to cienka warstwa wokół kosza zgodnego z S3 plus ten sam model zabezpieczeń na poziomie wierszy co baza danych. Oznacza to, że te same pułapki RLS, które dotyczą tabel, dotyczą dostępu do plików — i kilka specyficznych dla magazynu, które pojawiają się, gdy narzędzia kodowania AI podłączają przesyłanie. Ta lista kontrolna ma 22 punkty w pięciu sekcjach: konfiguracja kosza, polityki RLS, walidacja przesyłania, podpisane URL i higiena operacyjna. Każdy można zweryfikować w mniej niż 15 minut.

Każdy element poniżej jest niezbędny. Dla podstawowej mechaniki RLS zobacz Skaner Supabase RLS. Dla klasy ujawniania kluczy sąsiadującej z magazynem zobacz Klucz roli serwisowej Supabase ujawniony w JavaScript.

Konfiguracja kosza

Zacznij od właściwych domyślnych. Niewłaściwie skonfigurowany kosz wycieka pliki niezależnie od tego, czy Twój RLS jest poprawny, czy nie.

  1. Ustaw każdy kosz domyślnie na prywatny. W panelu Supabase → Storage → Buckets ustaw przełącznik Public bucket na wyłączony, chyba że masz wyraźny powód (zasoby marketingowe, publiczne awatary bez PII). Publiczne kosze omijają RLS dla operacji odczytu — każdy z nazwą kosza może wymieniać i pobierać.
  2. Ustaw twardy limit rozmiaru pliku dla każdego kosza. Panel → Ustawienia kosza → Limit rozmiaru pliku. 50 MB to rozsądna domyślna dla przesyłania przez użytkowników; podnoś celowo dla przypadków użycia wideo / dużych plików. Bez limitu pojedyncze złośliwe przesłanie może wyczerpać Twoją kwotę magazynu lub miesięczną przepustowość.
  3. Ogranicz dozwolone typy MIME na kosz. Lista dozwolonych typów MIME — jawna lista dozwolonych, nie lista zablokowanych. image/jpeg, image/png, image/webp dla koszy tylko z obrazami. Nigdy nie zezwalaj na text/html, application/javascript ani image/svg+xml w koszu z treścią użytkownika — wykonują się w przeglądarce po podaniu przez podpisany URL.
  4. Użyj jednego kosza na typ treści, nie jednego wspólnego kosza. Ustawienia na kosz (rozmiar, typy MIME, polityki RLS) to ziarnistość, którą masz. Kosz user-avatars, kosz document-uploads i kosz public-assets są łatwiejsze do zablokowania niż jeden mieszany kosz.
  5. Zweryfikuj konfigurację CORS, jeśli frontend przesyła. Jeśli użytkownicy przesyłają bezpośrednio z przeglądarki na podpisany URL, CORS kosza musi wymieniać Twoje produkcyjne pochodzenie. * jest akceptowalne tylko dla publicznych koszy — nigdy dla koszy zawierających PII użytkowników.

Polityki RLS na storage.objects

Supabase Storage przechowuje metadane plików w tabeli storage.objects. RLS na tej tabeli kontroluje, kto może odczytywać, przesyłać, aktualizować lub usuwać pliki. Bez RLS flaga publiczna/prywatna kosza jest Twoją jedyną ochroną.

  1. Potwierdź, że RLS jest włączony na storage.objects. SELECT rowsecurity FROM pg_tables WHERE schemaname = 'storage' AND tablename = 'objects'; musi zwrócić true. Supabase włącza go domyślnie w nowych projektach; zweryfikuj, że nie został wyłączony.
  2. Napisz politykę SELECT z zakresem auth.uid() dla prywatnych koszy. CREATE POLICY "users_read_own_files" ON storage.objects FOR SELECT USING (auth.uid()::text = (storage.foldername(name))[1]);. Konwencja to przechowywanie plików pod [user-id]/[filename] i używanie storage.foldername() do wyodrębnienia właściciela ze ścieżki.
  3. Napisz politykę INSERT, która wymusza tę samą konwencję ścieżki. CREATE POLICY "users_upload_own" ON storage.objects FOR INSERT WITH CHECK (auth.uid()::text = (storage.foldername(name))[1]);. Bez WITH CHECK uwierzytelniony użytkownik może przesyłać do folderu innego użytkownika.
  4. Dodaj polityki UPDATE i DELETE, jeśli Twoja aplikacja obsługuje edycję lub usuwanie plików. Każde polecenie potrzebuje własnej polityki. Pominięcie DELETE oznacza, że uwierzytelnieni użytkownicy nie mogą usuwać własnych plików; pominięcie UPDATE oznacza, że nadpisywanie plików po cichu się nie udaje.
  5. Przetestuj dostęp międzyużytkownikowy w dwóch sesjach przeglądarki. Zaloguj się jako Użytkownik A, prześlij plik, skopiuj ścieżkę. Zaloguj się jako Użytkownik B w innej przeglądarce, spróbuj pobrać plik przez REST API. Odpowiedź musi być 403 lub 404, nigdy 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]);

Walidacja przesyłania

Waliduj każde przesłanie po stronie serwera, nawet gdy kosz ma ograniczenia MIME i rozmiaru. Narzędzia kodowania AI generują domyślnie walidację tylko po stronie klienta; to nic nie chroni.

  1. Ponownie sprawdź typ MIME po stronie serwera z rzeczywistych bajtów pliku, a nie nagłówka Content-Type. Użyj biblioteki takiej jak file-type (Node) lub wąchania magic byte. Atakujący może żądać Content-Type: image/jpeg na pliku, który w rzeczywistości jest poliglotem HTML / JavaScript.
  2. Usuń metadane EXIF z przesłanych obrazów. EXIF może zawierać współrzędne GPS, numery seryjne urządzeń i znaczniki czasu. Użyj sharp z .withMetadata(false) lub exif-parser, aby usunąć przed przechowywaniem.
  3. Odrzucaj SVG zawierające tagi script lub procedury obsługi onload. SVG to XML — i wiele aplikacji generowanych przez AI pozwala na przesyłanie SVG jako „tylko obraz". Użyj DOMPurify po stronie serwera lub całkowicie odmawiaj przesyłania SVG.
  4. Użyj deterministycznych, niezgadywalnych nazw plików. Nie zachowuj oryginalnej nazwy pliku. Użyj UUID lub skrótu zawartości pliku. Oryginalne nazwy plików wyciekają informacje ("passport_scan_2024_01_15.jpg"), a przewidywalne nazwy umożliwiają wyliczanie.

Podpisane URL

Podpisane URL to sposób, w jaki klienci uzyskują dostęp do prywatnych koszy. Wygaśnięcie, zakres kosza i to, co jest logowane, mają znaczenie.

  1. Domyślnie wygaśnięcie podpisanego URL na 1 godzinę lub mniej. createSignedUrl(path, expiresIn) w Supabase JS SDK przyjmuje sekundy. Nigdy nie używaj wartości takich jak 31536000 (jeden rok) — URL staje się trwałym częściowo publicznym linkiem.
  2. Nigdy nie przechowuj podpisanych URL w swojej bazie danych. Generuj świeże po stronie serwera przy każdym żądaniu. Przechowywany podpisany URL z rocznym wygaśnięciem, który wycieknie przez zrzut bazy danych, daje długoterminowy dostęp.
  3. Loguj generowanie podpisanych URL, nie tylko przesyłanie plików. Jeśli później podejrzewasz kompromitację, musisz wiedzieć, kto wygenerował który URL kiedy. Loguj auth.uid() + kosz + ścieżkę obiektu + znacznik czasu.
  4. Użyj opcji downloadAs podczas obsługi plików przesłanych przez użytkowników. createSignedUrl(path, expiresIn, { download: '.jpg' }) wymusza nagłówek Content-Disposition: attachment, więc plik się pobiera zamiast renderować — pokonuje klasę wykonywania HTML / SVG / HTML-w-PDF.

Higiena operacyjna

Konfiguracja magazynu z czasem dryfuje. Te cztery elementy operacyjne utrzymują powierzchnię szczelną.

  1. Sprawdzaj kosze kwartalnie. Panel → Storage → Buckets. Potwierdź, że stan publiczny/prywatny i listy typów MIME pasują do tego, czego oczekuje aplikacja. Kosze utworzone „tymczasowo" stają się trwałe, jeśli nikt ich nie usunie.
  2. Monitoruj operacje anonimowego listowania. Logi magazynu (Panel → Logs → Storage) rejestrują żądania LIST. Skok anonimowych żądań listowania wobec prywatnego kosza oznacza, że ktoś go sondy z zewnątrz.
  3. Ustaw politykę przechowywania dla efemerycznych przesyłań. Tymczasowe kosze (podgląd obrazu, robocze przesyłania) powinny automatycznie usuwać się po 24-72 godzinach przez zaplanowaną funkcję. Nieokreślone przechowywanie jest odpowiedzialnością zgodnie z obowiązkami minimalizacji danych GDPR / CCPA.
  4. Uruchamiaj skanowanie FixVibe co miesiąc. Kontrola baas.supabase-storage-public sondy kosze, które odpowiadają na anonimowe GET + LIST. Nowe kosze są dodawane; stare zmieniają widoczność — tylko ciągłe skanowanie wyłapuje dryf.

Następne kroki

Uruchom skanowanie FixVibe wobec swojego produkcyjnego URL — anonimowe listy magazynu pojawiają się pod baas.supabase-storage-public. Połącz tę listę kontrolną ze Skanerem Supabase RLS dla warstwy tabel i Kluczem roli serwisowej Supabase ujawnionym w JavaScript dla sąsiedztwa ujawniania kluczy. Dla błędnych konfiguracji magazynu u innych dostawców BaaS zobacz Skaner błędnych konfiguracji BaaS.

// skanuj swoją powierzchnię baas

Znajdź otwartą tabelę, zanim zrobi to ktoś inny.

Podaj produkcyjny adres URL. FixVibe wyliczy dostawców BaaS, z którymi komunikuje się Twoja aplikacja, pobierze odciski palców ich publicznych punktów końcowych i zgłosi, co nieuwierzytelniony klient może odczytać lub zapisać. Bezpłatnie, bez instalacji, bez karty.

  • Bezpłatny poziom — 3 skanowania / miesiąc, bez karty przy rejestracji.
  • Pasywne pobieranie odcisków palców BaaS — bez weryfikacji domeny.
  • Supabase, Firebase, Clerk, Auth0, Appwrite i inne.
  • Podpowiedzi naprawy AI przy każdym znalezisku — wklej z powrotem do Cursor / Claude Code.
Lista kontrolna bezpieczeństwa kosza magazynu Supabase: 22 punkty — Docs · FixVibe