Zum Inhalt springen

Sicherheitsgrundlagen

Dein Supabase-Storage-Bucket ist öffentlich. Ist das ein Problem?

Ein öffentlicher Supabase-Storage-Bucket heißt: Wer die URL einer Datei hat, kann sie öffnen. Er heißt nicht, dass jemand den Inhalt auflisten darf.

Vlad Tkachenko7 Min. Lesezeit
Dokumente in einem Behälter. Ein Balken deckt den größten Teil der Öffnung ab und endet vorzeitig, vier Dokumente stehen in der Lücke.

Kurz gesagt

  • Ein öffentlicher Supabase-Storage-Bucket bedeutet eine Sache: Wer die URL einer Datei hat, kann sie ohne Login öffnen. Über das, was sonst noch darin liegt, sagt er nichts.
  • Das Auflisten erlaubt eine Zugriffsrichtlinie und nicht der Schalter, also kann ein privater Bucket auflistbar sein und ein öffentlicher nicht.
  • Das Auflisten ist das, was heute zu beheben ist, denn es erspart einem Fremden das Raten eines einzigen Dateinamens.

Jemand hat deine App geöffnet, eine Minute darin herumgestochert und dir eine Nachricht geschickt: Dein Supabase-Storage-Bucket ist öffentlich, und jeder kann auflisten, was darin liegt.

Das sind zwei getrennte Aussagen. Die eine betrifft vermutlich eine Einstellung, die du absichtlich gewählt hast und behalten solltest. Die andere ist einen Nachmittag wert.

Hier ist der Punkt, den Anleitung um Anleitung falsch darstellt: öffentlich und auflistbar sind zwei verschiedene Schalter, und der beunruhigende ist nicht der, den dir alle umzulegen empfehlen. Einen Bucket auf privat zu stellen schließt den ersten. Den zweiten kann es weit offen stehen lassen, und ein Bucket, der nie in seinem Leben öffentlich war, kann heute Nachmittag auflistbar sein.

Ist ein öffentlicher Supabase-Storage-Bucket ein Sicherheitsproblem?

Für sich genommen nein. Es bedeutet eine ganz bestimmte Sache, und die ist sehr oft genau das, was du wolltest.

Einen Bucket öffentlich zu machen gibt jeder Datei darin eine URL, die ohne Login funktioniert. Das ist die ganze Funktion. So lädt der Avatar unter einem Kommentar für jemanden ohne Konto, so erscheint dein Logo in einer E-Mail, so sieht ein Produktfoto, wer noch überlegt, ob er sich anmeldet.

Ein öffentlicher Bucket ähnelt eher einer nicht eingetragenen Telefonnummer als einer unverschlossenen Tür. Die Leitung verbindet jeden, der wählt, und es gibt kein Verzeichnis, in dem die Nummer steht. Ob das in Ordnung ist, hängt an einer Frage: wie leicht jemand auf die Nummer kommt, ohne sie genannt bekommen zu haben.

Supabase legt neue Buckets privat an und setzt eine Warnung an den Schalter. Ein öffentlicher Bucket ist also etwas, das jemand eingeschaltet hat: du, oder dein Builder, um eine Upload-Funktion zum Laufen zu bringen.

Was der Schalter „öffentlich" tatsächlich einschaltet

Eine Zeile der Tabelle unten, und sonst nichts darin.

Was jemand versuchtÖffentlicher BucketPrivater Bucket
Eine Datei öffnen, deren genaue URL vorliegtFunktioniert, ohne LoginBraucht eine Richtlinie oder einen signierten Link
Den Inhalt des Buckets auflisten lassenNur wenn eine Richtlinie es erlaubtNur wenn eine Richtlinie es erlaubt
Eine Datei hochladenNur wenn eine Richtlinie es erlaubtNur wenn eine Richtlinie es erlaubt
Eine Datei löschen oder ersetzenNur wenn eine Richtlinie es erlaubtNur wenn eine Richtlinie es erlaubt

Die eigene Hilfeseite von Supabase sagt es so klar, wie es sich sagen lässt: Ein öffentlicher Bucket bedeutet, dass es eine öffentliche URL gibt, über die sich die Datei herunterladen lässt, und jeder andere Vorgang muss weiterhin die Richtlinien dieses Buckets erfüllen.

Der Schalter ist also eine kleinere Kontrolle, als sein Name vermuten lässt. Drei dieser vier Zeilen werden ganz woanders entschieden, unter Storage → Policies, und die Nachricht, die du bekommen hast, betraf mit ziemlicher Sicherheit die zweite.

Der Schalter oben ändert eine Sache. Das Feld darunter entscheidet, ob ein Fremder die Liste bekommt, und es tut das gleich, ob der Bucket öffentlich oder privat ist.

Warum ein privater Bucket trotzdem auflistbar sein kann

Weil das Auflisten von einer Zugriffsrichtlinie erlaubt wird, und die Richtlinie, die die meisten einfügen, erlaubt es allen.

Jeder Lesezugriff auf Storage läuft über Row Level Security an einer Tabelle namens storage.objects. Einen Bucket aufzulisten ist ein Lesezugriff, abgelegt unter SELECT, genau wie das Herunterladen einer Datei. Der eigene Schnelleinstieg von Supabase zeigt eine Richtlinie in dieser Form, aus der die Kopien meist stammen:

create policy "Public Access"
  on storage.objects for select
  using ( bucket_id = 'public' );

Lies, was da steht. Jede beliebige Anfrage darf alles in diesem Bucket lesen. Sie fragt nicht, wer fragt, und sie fragt nicht, ob die Datei überhaupt etwas mit dem Fragenden zu tun hat. Lesen umfasst das Herunterladen einer Datei, und es umfasst das Herausgeben der Dateiliste, denn das ist dieselbe Berechtigung mit zwei Hüten.

Der Schalter des Buckets kommt dabei nie ins Spiel. Eine solche Richtlinie auf einem privaten Bucket macht den Bucket auflistbar, und das Dashboard nennt ihn weiterhin wahrheitsgemäß privat.

Das Wort, das den Schaden anrichtet, ist „jede". Eine Anfrage mit dem öffentlichen Schlüssel, der im Code deiner App mitgeliefert wird, erfüllt diese Richtlinie mühelos, und dieser Schlüssel soll für alle lesbar sein, weshalb genau die Richtlinie die Arbeit übernehmen soll. Das ist dieselbe Form wie beim Problem nebenan, wo Row Level Security eingeschaltet ist und trotzdem alles durchlässt.

Was jemand aus einer Liste deiner Dateien gewinnt

Namen, vor allem. Und das ist deutlich mehr, als es klingt.

Dateinamen beschreiben meist ihren Inhalt, weil jemand sie gewählt hat, während er an das Innere dachte: rechnung-maerz-acme.pdf, reisepass-vorne.jpg, lohn-final-v2.xlsx. Eine Liste davon ist eine brauchbare Zusammenfassung deines Geschäfts, und ihre Länge sagt ungefähr, wie viele Kunden du hast. Ist der Bucket zusätzlich öffentlich, ist jeder Name auf dieser Liste ein funktionierender Link.

Zurück zur Telefonnummer. Nicht eingetragen zu sein ist etwas wert, bis das Verzeichnis erscheint, und danach war es nie wichtig, wie schwer die Nummer zu erraten war.

Ohne Auflisten muss ein Fremder irgendwie auf einen Dateinamen kommen. Mit Auflisten bekommt er jeden Dateinamen, den du hast.

So zeigt es sich im Alltag: Ein Dokument, das einem Kunden gehört, taucht vor einem anderen auf. Jemand nennt dir den Namen einer Datei, den er nicht wissen konnte. Ein Ordner mit Uploads wird komplett kopiert, von einem automatischen Crawler, der jedes Supabase-Projekt durchging, das er finden konnte.

Unser kostenloser Scan fragt deinen Storage nach einer Auflistung, mit nichts außer dem Schlüssel, der ohnehin im Code deiner App steht, und sagt dir, welche Buckets mit Inhalt geantwortet haben. Er liest Namen und lädt nie eine Datei herunter. Er dauert etwa 20 Sekunden und braucht kein Konto: App scannen.

Woran du erkennst, welchen Fall du hast

Zwei Prüfungen im Supabase-Dashboard, und die zweite betrifft das, worum es in der Nachricht wirklich ging.

Storage → Buckets. Die öffentlichen sind als öffentlich gekennzeichnet. Frag dich bei jedem, ob wirklich jede einzelne Datei darin etwas ist, das du einem Fremden zeigen würdest. Nicht die meisten Dateien. Jede, auch das, was nächste Woche von einer Funktion dort landet, die du noch nicht gebaut hast.

Storage → Policies. Lies jede SELECT-Richtlinie, die den Bucket betrifft. Eine Bedingung, die nur den Namen des Buckets nennt, lässt jeden darin lesen. Eine Bedingung, die den Eigentümer der Datei mit dem Anfragenden vergleicht, tut echte Arbeit. Eine leere Richtlinienliste auf einem privaten Bucket heißt, dass überhaupt nichts gelesen wird, und das ist streng und sicher.

Muss deine App eine private Datei der richtigen Person zeigen, ist das Werkzeug dafür ein signierter Link: Dein Server holt sich von Supabase eine URL, die eine festgelegte Zahl von Minuten funktioniert und danach nicht mehr. Damit bleibt die Datei privat und kommt trotzdem auf die Seite.

Wann öffentlich die richtige Antwort ist

Häufiger, als ein Sicherheitsartikel üblicherweise zugibt, und das gehört gesagt.

Ist die Datei für alle gedacht, ist öffentlich richtig, und ein Umweg darum bringt dir eine langsamere App und mehr Code zu pflegen. Avatare, Logos, Titelbilder, alles, was ein abgemeldeter Besucher sehen soll: ab in einen öffentlichen Bucket, und dann nicht mehr daran denken.

Das Einzige, was auch dort zu tun ist, ist das Auflisten auszulassen. Ein öffentlicher Bucket voller Avatare ist eine Funktion. Derselbe Bucket, der die vollständige Liste aller herausgibt, die je einen hochgeladen haben, ist etwas anderes, das du nicht bestellt hast.

Gehört die Datei einer bestimmten Person, kommt sie in einen privaten Bucket mit einer Richtlinie, die prüft, wer fragt, und erreicht die Seite über einen signierten Link. Der Test ist eine einzige Frage: Für wen ist diese Datei? Für alle, oder für ein bestimmtes Konto.

Was diese Woche zu tun ist

Was zu tun ist

  • Öffne Storage → Buckets und schreib auf, welche öffentlich sind. Entscheide bei jedem, ob er Dateien für alle enthält oder Dateien für eine einzelne Person.
  • Lies jede SELECT-Richtlinie auf storage.objects. Eine Bedingung, die nur den Bucket nennt, gibt die Dateiliste an jeden heraus; genau die hat die Nachricht wahr gemacht, die du bekommen hast.
  • Verschiebe alles, was einer einzelnen Person gehört, in einen privaten Bucket und liefere es über signierte Links aus, die von selbst ablaufen.
  • Hör auf, hochgeladene Dateien nach ihrem Inhalt zu benennen. Eine zufällige Kennung kostet nichts und sorgt dafür, dass eine abgeflossene Liste viel weniger preisgibt.
  • Sichere deinen Storage getrennt von deiner Datenbank. Deine Uploads stecken weder in deinem Code noch in deinen Postgres-Tabellen, also kopiert nichts, was diese beiden kopiert, auch deine Dateien.

Öffne Storage → Policies in deinem Projekt und lies, was dort steht, bevor du einen einzigen Schalter umlegst. Dieser eine Bildschirm entscheidet, ob die zweite Hälfte deiner Nachricht zutraf. Die 10-Minuten-Sicherheits-Checkliste deckt das neben dem Rest ab, was eine frisch gestartete App offen zu lassen pflegt, und falls ein Bucket tatsächlich lesbar war, ist was ein Fremder in deiner Datenbank erreicht das Nächste, denn die Richtlinie, die eine Dateiliste herausgibt, hat dieselbe Form wie die, die eine Tabelle herausgibt.

FAQ

Jemand sagt, mein Supabase-Storage-Bucket sei öffentlich. Soll ich ihn privat machen?

Nicht bevor du weißt, was darin liegt. Öffentliche Buckets haben ihren Grund: Profilbilder, Logos, Produktfotos und alles andere, was deine App einem nicht angemeldeten Besucher zeigt. Für solche Dateien ist öffentlich die richtige Einstellung. Für Rechnungen, Ausweisdokumente, Exporte oder alles, was einer bestimmten Person gehört, ist sie die falsche, und privat plus signierte Links ist dann das Richtige. Die Frage war nie, ob öffentlich schlecht ist. Sie lautet, ob genau diese Dateien für jeden gedacht waren, der danach fragt.

Was ist der Unterschied zwischen einem öffentlichen und einem auflistbaren Bucket?

Öffentlich entscheidet, ob sich eine Datei für jemanden öffnet, der ihre URL bereits hat. Auflistbar entscheidet, ob ein Fremder dein Projekt nach dem gesamten Inhalt eines Buckets fragen kann und eine Antwort bekommt. Beides wird an verschiedenen Stellen im Supabase-Dashboard eingestellt, und das Zweite macht aus einem Raten ein Verzeichnis.

Kann jemand die URLs der Dateien in meinem öffentlichen Bucket erraten?

Das hängt ganz davon ab, wie deine App sie benennt. Lädt sie unter dem ursprünglichen Dateinamen hoch oder unter etwas Geordnetem wie rechnung-4.pdf, dann ja, und es kostet sehr wenig Mühe. Lädt sie unter einer langen zufälligen Kennung hoch, ist Raten unpraktikabel. Genau deshalb wiegt das Auflisten so schwer: Es entfernt den Schritt des Ratens vollständig.

Wie verhindere ich, dass jemand meinen Bucket auflistet?

Das Auflisten läuft über eine Row-Level-Security-Richtlinie an der Tabelle storage.objects, dort wird es also behoben und nicht am Bucket selbst. Öffne Storage → Policies in deinem Supabase-Dashboard und lies jede SELECT-Richtlinie, die den Bucket betrifft. Eine, deren einzige Bedingung der Name des Buckets ist, lässt jede Anfrage darin lesen, die Dateiliste eingeschlossen. Verenge sie so, dass sie die Datei mit der fragenden Person vergleicht, oder entferne sie und gib stattdessen signierte Links von deinem Server aus.

Sind meine hochgeladenen Dateien in meinem Datenbank-Backup enthalten?

Nein. Storage liegt außerhalb deiner Postgres-Datenbank, ein Datenbank-Backup enthält also die Zeilen, die auf deine Dateien zeigen, und keine der Dateien selbst. Es zurückzuspielen gibt dir eine Tabelle voller Verweise auf Dinge, die nicht mehr da sind. Deine Uploads müssen getrennt kopiert werden, und die meisten erfahren das an dem Tag, an dem es darauf ankommt.

Geschrieben von

Vlad Tkachenko

Gründer, Reeve

Ich schaue mir den ganzen Tag Apps an, die mit Lovable, Bolt, v0, Cursor und Replit gebaut wurden, und die kurze Liste von Fehlern, die darin immer wieder auftauchen.

Mehr über den Autor

Weiterlesen

Unsicher, wie es um deine eigene App steht?

Starte einen kostenlosen Scan und erhalte in rund 20 Sekunden eine verständliche Note von A bis F. Ohne Konto, ohne Karte.

App kostenlos scannen

Automatisierte externe Prüfung, kein vollständiges Audit. Das Fehlen von Funden ist keine Garantie für Sicherheit.