Translated ['', 'src/pentesting-ci-cd/supabase-security.md'] to de

This commit is contained in:
Translator
2025-09-29 23:06:18 +00:00
parent c25ca7b26a
commit e49d038938

View File

@@ -1,45 +1,45 @@
# Supabase-Sicherheit
# Supabase Sicherheit
{{#include ../banners/hacktricks-training.md}}
## Grundinformationen
Laut ihrer [**Landing Page**](https://supabase.com/): Supabase ist eine Open-Source-Alternative zu Firebase. Starten Sie Ihr Projekt mit einer Postgres-Datenbank, Authentifizierung, sofortigen APIs, Edge-Funktionen, Echtzeit-Abonnements, Speicher und Vektor-Embeddings.
As per their [**landing page**](https://supabase.com/): Supabase is an open source Firebase alternative. Start your project with a Postgres database, Authentication, instant APIs, Edge Functions, Realtime subscriptions, Storage, and Vector embeddings.
### Subdomain
Im Grunde erhält der Benutzer, wenn ein Projekt erstellt wird, eine supabase.co-Subdomain wie: **`jnanozjdybtpqgcwhdiz.supabase.co`**
Im Grunde erhält der Benutzer beim Erstellen eines Projekts eine supabase.co-Subdomain wie: **`jnanozjdybtpqgcwhdiz.supabase.co`**
## **Datenbankkonfiguration**
## **Database configuration**
> [!TIP]
> **Diese Daten können über einen Link wie `https://supabase.com/dashboard/project/<project-id>/settings/database` abgerufen werden**
Diese **Datenbank** wird in einer AWS-Region bereitgestellt, und um eine Verbindung herzustellen, wäre es möglich, sich zu verbinden: `postgres://postgres.jnanozjdybtpqgcwhdiz:[YOUR-PASSWORD]@aws-0-us-west-1.pooler.supabase.com:5432/postgres` (dies wurde in us-west-1 erstellt).\
Das Passwort ist ein **Passwort, das der Benutzer zuvor eingegeben hat**.
Diese **Datenbank** wird in einer AWS-Region bereitgestellt, und um sich damit zu verbinden ist es möglich, sich zu verbinden mit: `postgres://postgres.jnanozjdybtpqgcwhdiz:[YOUR-PASSWORD]@aws-0-us-west-1.pooler.supabase.com:5432/postgres` (dies wurde in us-west-1 erstellt).
Das Passwort ist ein **vom Benutzer zuvor gesetztes Passwort**.
Da die Subdomain bekannt ist und als Benutzername verwendet wird und die AWS-Regionen begrenzt sind, könnte es möglich sein, das **Passwort zu brute-forcen**.
Da die Subdomain bekannt ist, als Username verwendet wird und die AWS-Regionen begrenzt sind, könnte es möglich sein, einen **brute force the password** zu versuchen.
Dieser Abschnitt enthält auch Optionen zum:
- Zurücksetzen des Datenbankpassworts
- Konfigurieren von Verbindungspooling
- Konfigurieren von SSL: Ablehnen von Klartextverbindungen (standardmäßig sind sie aktiviert)
- Konfigurieren der Festplattengröße
- Anwenden von Netzwerkbeschränkungen und -sperren
- Datenbank-Passwort zurücksetzen
- Connection pooling konfigurieren
- SSL konfigurieren: Reject plan-text connections (standardmäßig sind sie aktiviert)
- Festplattengröße konfigurieren
- Netzwerkbeschränkungen und Sperren anwenden
## API-Konfiguration
## API Configuration
> [!TIP]
> **Diese Daten können über einen Link wie `https://supabase.com/dashboard/project/<project-id>/settings/api` abgerufen werden**
Die URL zum Zugriff auf die Supabase-API in Ihrem Projekt wird wie folgt aussehen: `https://jnanozjdybtpqgcwhdiz.supabase.co`.
Die URL, um auf die supabase API in deinem Projekt zuzugreifen, sieht beispielsweise so aus: `https://jnanozjdybtpqgcwhdiz.supabase.co`.
### anon API-Schlüssel
### anon api keys
Es wird auch einen **anon API-Schlüssel** (`role: "anon"`) generieren, wie: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImpuYW5vemRyb2J0cHFnY3doZGl6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTQ5OTI3MTksImV4cCI6MjAzMDU2ODcxOX0.sRN0iMGM5J741pXav7UxeChyqBE9_Z-T0tLA9Zehvqk` den die Anwendung verwenden muss, um den in unserem Beispiel exponierten API-Schlüssel zu kontaktieren.
Es wird außerdem ein **anon API key** (`role: "anon"`), z. B.: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImpuYW5vemRyb2J0cHFnY3doZGl6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTQ5OTI3MTksImV4cCI6MjAzMDU2ODcxOX0.sRN0iMGM5J741pXav7UxeChyqBE9_Z-T0tLA9Zehvqk`, generiert, den die Anwendung verwenden muss, um mit der API zu kommunizieren, wie in unserem Beispiel unten gezeigt.
Es ist möglich, die REST-API zu finden, um diese API in den [**Docs**](https://supabase.com/docs/reference/self-hosting-auth/returns-the-configuration-settings-for-the-gotrue-server) zu kontaktieren, aber die interessantesten Endpunkte wären:
Es ist möglich, die REST-API, um diese API anzusprechen, in den [**docs**](https://supabase.com/docs/reference/self-hosting-auth/returns-the-configuration-settings-for-the-gotrue-server) zu finden, aber die interessantesten Endpoints wären:
<details>
@@ -72,7 +72,7 @@ Priority: u=1, i
<details>
<summary>Login (/auth/v1/token?grant_type=password)</summary>
<summary>Anmeldung (/auth/v1/token?grant_type=password)</summary>
```
POST /auth/v1/token?grant_type=password HTTP/2
Host: hypzbtgspjkludjcnjxl.supabase.co
@@ -99,61 +99,171 @@ Priority: u=1, i
```
</details>
Wenn Sie also einen Kunden entdecken, der Supabase mit der Subdomain verwendet, die ihm zugewiesen wurde (es ist möglich, dass eine Subdomain des Unternehmens ein CNAME über ihre Supabase-Subdomain hat), sollten Sie versuchen, **ein neues Konto auf der Plattform über die Supabase-API zu erstellen**.
Also, whenever you discover a client using supabase with the subdomain they were granted (it's possible that a subdomain of the company has a CNAME over their supabase subdomain), you might try to **create a new account in the platform using the supabase API**.
### secret / service_role API-Schlüssel
### secret / service_role API-Keys
Ein geheimer API-Schlüssel wird ebenfalls mit **`role: "service_role"`** generiert. Dieser API-Schlüssel sollte geheim sein, da er in der Lage ist, **Row Level Security** zu umgehen.
A secret API key will also be generated with **`role: "service_role"`**. This API key should be secret because it will be able to bypass **Row Level Security**.
Der API-Schlüssel sieht so aus: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImpuYW5vemRyb2J0cHFnY3doZGl6Iiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTcxNDk5MjcxOSwiZXhwIjoyMDMwNTY4NzE5fQ.0a8fHGp3N_GiPq0y0dwfs06ywd-zhTwsm486Tha7354`
The API key looks like this: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImpuYW5vemRyb2J0cHFnY3doZGl6Iiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTcxNDk5MjcxOSwiZXhwIjoyMDMwNTY4NzE5fQ.0a8fHGp3N_GiPq0y0dwfs06ywd-zhTwsm486Tha7354`
### JWT-Geheimnis
### JWT Secret
Ein **JWT-Geheimnis** wird ebenfalls generiert, damit die Anwendung **benutzerdefinierte JWT-Token erstellen und signieren** kann.
A **JWT Secret** will also be generate so the application can **create and sign custom JWT tokens**.
## Authentifizierung
### Anmeldungen
### Registrierungen
> [!TIP]
> Standardmäßig erlaubt Supabase **neuen Benutzern, Konten** in Ihrem Projekt über die zuvor genannten API-Endpunkte zu erstellen.
> By **default** supabase will allow **new users to create accounts** on your project by using the previously mentioned API endpoints.
Diese neuen Konten müssen jedoch standardmäßig **ihre E-Mail-Adresse validieren**, um sich in das Konto einloggen zu können. Es ist möglich, **"Anonyme Anmeldungen erlauben"** zu aktivieren, um es Personen zu ermöglichen, sich ohne Verifizierung ihrer E-Mail-Adresse anzumelden. Dies könnte den Zugriff auf **unerwartete Daten** gewähren (sie erhalten die Rollen `public` und `authenticated`).\
Das ist eine sehr schlechte Idee, da Supabase pro aktivem Benutzer Gebühren erhebt, sodass Personen Benutzer erstellen und sich anmelden könnten, und Supabase dafür Gebühren erhebt:
However, these new accounts, by default, **will need to validate their email address** to be able to login into the account. It's possible to enable **"Allow anonymous sign-ins"** to allow people to login without verifying their email address. This could grant access to **unexpected data** (they get the roles `public` and `authenticated`).\
This is a very bad idea because supabase charges per active user so people could create users and login and supabase will charge for those:
<figure><img src="../images/image (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
#### Auth: Server-seitige Durchsetzung der Registrierung
Hiding the signup button in the frontend is not enough. If the **Auth server still allows signups**, an attacker can call the API directly with the public `anon` key and create arbitrary users.
Schneller Test (von einem nicht authentifizierten Client):
```bash
curl -X POST \
-H "apikey: <SUPABASE_ANON_KEY>" \
-H "Authorization: Bearer <SUPABASE_ANON_KEY>" \
-H "Content-Type: application/json" \
-d '{"email":"attacker@example.com","password":"Sup3rStr0ng!"}' \
https://<PROJECT_REF>.supabase.co/auth/v1/signup
```
Expected hardening:
- Deaktivieren Sie E-Mail/Passwort-Registrierungen im Dashboard: Authentication → Providers → Email → Disable sign ups (invite-only), oder setzen Sie das entsprechende GoTrue-Setting.
- Stellen Sie sicher, dass die API jetzt 4xx auf den vorherigen Aufruf zurückgibt und kein neuer Benutzer erstellt wird.
- Wenn Sie sich auf Invites oder SSO verlassen, stellen Sie sicher, dass alle anderen Providers deaktiviert sind, sofern sie nicht explizit benötigt werden.
## RLS und Views: Schreib-Bypass via PostgREST
Die Verwendung einer Postgres VIEW, um sensible Spalten zu „verbergen“, und deren Exposition über PostgREST kann verändern, wie Berechtigungen bewertet werden. In PostgreSQL:
- Gewöhnliche Views werden standardmäßig mit den Privilegien des View-Owners ausgeführt (definer semantics). In PG ≥15 können Sie `security_invoker` aktivieren.
- Row Level Security (RLS) gilt für Basistabellen. Tabellenbesitzer umgehen RLS, es sei denn, `FORCE ROW LEVEL SECURITY` ist auf der Tabelle gesetzt.
- Updatable views können INSERT/UPDATE/DELETE akzeptieren, die dann auf die Basistabelle angewendet werden. Ohne `WITH CHECK OPTION` können Schreibvorgänge, die nicht mit dem View-Prädikat übereinstimmen, trotzdem erfolgreich sein.
Beobachtetes Risikomuster in der Praxis:
- Eine View mit reduzierten Spalten wird über Supabase REST bereitgestellt und `anon`/`authenticated` zugewiesen.
- PostgREST erlaubt DML auf der updatable view und die Operation wird mit den Privilegien des View-Owners ausgewertet, wodurch die vorgesehenen RLS-Policies auf der Basistabelle effektiv umgangen werden.
- Ergebnis: Clients mit niedrigen Rechten können massenhaft Zeilen bearbeiten (z. B. Profil-Bios/Avatare), die sie nicht hätten ändern dürfen.
Illustratives Schreiben via View (versucht von einem öffentlichen Client):
```bash
curl -X PATCH \
-H "apikey: <SUPABASE_ANON_KEY>" \
-H "Authorization: Bearer <SUPABASE_ANON_KEY>" \
-H "Content-Type: application/json" \
-H "Prefer: return=representation" \
-d '{"bio":"pwned","avatar_url":"https://i.example/pwn.png"}' \
"https://<PROJECT_REF>.supabase.co/rest/v1/users_view?id=eq.<victim_user_id>"
```
Hardening-Checkliste für Views und RLS:
- Bevorzuge das Freigeben von Basistabellen mit expliziten, nach dem Prinzip der geringsten Rechte vergebenen Berechtigungen und präzisen RLS-Richtlinien.
- Wenn du eine View freigeben musst:
- Mache sie nicht aktualisierbar (z. B. durch Ausdrücke/Joins) oder verweigere `INSERT/UPDATE/DELETE` auf der view für alle nicht vertrauenswürdigen Rollen.
- Erzwinge `ALTER VIEW <v> SET (security_invoker = on)`, sodass die Rechte des Invokers statt der des Owners verwendet werden.
- Auf Basistabellen verwende `ALTER TABLE <t> FORCE ROW LEVEL SECURITY;`, damit selbst Owner der RLS unterliegen.
- Wenn du Schreibzugriffe über eine updatable view erlaubst, füge `WITH [LOCAL|CASCADED] CHECK OPTION` hinzu und ergänze passende RLS auf den Basistabellen, um sicherzustellen, dass nur erlaubte Zeilen geschrieben/geändert werden können.
- In Supabase solltest du `anon`/`authenticated` keine Schreibrechte auf views gewähren, es sei denn, du hast das End-to-End-Verhalten mit Tests verifiziert.
Erkennungstipp:
- Versuche mit `anon` und einem `authenticated` Testuser alle CRUD-Operationen gegen jede exponierte Tabelle/View. Jeder erfolgreiche Schreibzugriff, bei dem du eine Verweigerung erwartet hättest, deutet auf eine Fehlkonfiguration hin.
### OpenAPI-gesteuertes CRUD-Probing von anon/auth-Rollen
PostgREST stellt ein OpenAPI-Dokument bereit, mit dem du alle REST-Ressourcen auflisten und anschließend automatisch erlaubte Operationen aus Sicht von Rollen mit geringen Rechten prüfen kannst.
Hole das OpenAPI-Dokument (funktioniert mit dem öffentlichen anon key):
```bash
curl -s https://<PROJECT_REF>.supabase.co/rest/v1/ \
-H "apikey: <SUPABASE_ANON_KEY>" \
-H "Authorization: Bearer <SUPABASE_ANON_KEY>" \
-H "Accept: application/openapi+json" | jq '.paths | keys[]'
```
Sondierungsmuster (Beispiele):
- Eine einzelne Zeile lesen (erwarte 401/403/200 abhängig von RLS):
```bash
curl -s "https://<PROJECT_REF>.supabase.co/rest/v1/<table>?select=*&limit=1" \
-H "apikey: <SUPABASE_ANON_KEY>" \
-H "Authorization: Bearer <SUPABASE_ANON_KEY>"
```
- Test UPDATE ist blockiert (verwenden Sie einen nicht existierenden Filter, um zu vermeiden, dass Daten während des Tests verändert werden):
```bash
curl -i -X PATCH \
-H "apikey: <SUPABASE_ANON_KEY>" \
-H "Authorization: Bearer <SUPABASE_ANON_KEY>" \
-H "Content-Type: application/json" \
-H "Prefer: return=minimal" \
-d '{"__probe":true}' \
"https://<PROJECT_REF>.supabase.co/rest/v1/<table_or_view>?id=eq.00000000-0000-0000-0000-000000000000"
```
- Test INSERT ist blockiert:
```bash
curl -i -X POST \
-H "apikey: <SUPABASE_ANON_KEY>" \
-H "Authorization: Bearer <SUPABASE_ANON_KEY>" \
-H "Content-Type: application/json" \
-H "Prefer: return=minimal" \
-d '{"__probe":true}' \
"https://<PROJECT_REF>.supabase.co/rest/v1/<table_or_view>"
```
- Prüfen, ob DELETE blockiert ist:
```bash
curl -i -X DELETE \
-H "apikey: <SUPABASE_ANON_KEY>" \
-H "Authorization: Bearer <SUPABASE_ANON_KEY>" \
"https://<PROJECT_REF>.supabase.co/rest/v1/<table_or_view>?id=eq.00000000-0000-0000-0000-000000000000"
```
Recommendations:
- Automatisiere die vorherigen Probes für sowohl `anon` als auch einen minimalen `authenticated`-User und integriere sie in CI, um Regressionen zu erkennen.
- Behandle jede exponierte table/view/function als gleichwertige Angriffsfläche. Gehe nicht davon aus, dass eine view das gleiche RLS-Verhalten wie ihre Basistabellen "erbt".
### Passwörter & Sitzungen
Es ist möglich, die minimale Passwortlänge anzugeben (standardmäßig), Anforderungen (standardmäßig keine) und die Verwendung von geleakten Passwörtern zu untersagen.\
Es wird empfohlen, die Anforderungen zu **verbessern, da die Standardanforderungen schwach sind**.
Es ist möglich, die minimale Passwortlänge anzugeben (standardmäßig), Anforderungen (standardmäßig keine) und die Verwendung von leaked passwords zu verbieten.\
Es wird empfohlen, die Anforderungen zu **verschärfen, da die Standardwerte schwach sind**.
- Benutzersitzungen: Es ist möglich zu konfigurieren, wie Benutzersitzungen funktionieren (Timeouts, 1 Sitzung pro Benutzer...)
- Bot- und Missbrauchsschutz: Es ist möglich, Captcha zu aktivieren.
- User Sessions: Es ist möglich zu konfigurieren, wie User Sessions funktionieren (Timeouts, 1 Session pro Benutzer...)
- Bot and Abuse Protection: Es ist möglich, Captcha zu aktivieren.
### SMTP-Einstellungen
Es ist möglich, ein SMTP einzurichten, um E-Mails zu senden.
Es ist möglich, einen SMTP-Server zum Versenden von E-Mails einzurichten.
### Erweiterte Einstellungen
- Ablaufzeit für Zugriffstoken festlegen (standardmäßig 3600)
- Erkennen und Widerrufen potenziell kompromittierter Aktualisierungstoken und Timeout festlegen
- MFA: Angeben, wie viele MFA-Faktoren gleichzeitig pro Benutzer registriert werden können (standardmäßig 10)
- Maximale direkte Datenbankverbindungen: Maximale Anzahl von Verbindungen, die zur Authentifizierung verwendet werden (standardmäßig 10)
- Maximale Anforderungsdauer: Maximale Zeit, die für eine Auth-Anforderung zulässig ist (standardmäßig 10s)
- Ablaufzeit für access tokens festlegen (standardmäßig 3600)
- Erkennung und Widerruf potenziell kompromittierter refresh tokens und Timeouts konfigurieren
- MFA: Angeben, wie viele MFA-Faktoren pro Benutzer gleichzeitig registriert werden können (standardmäßig 10)
- Max Direct Database Connections: Maximale Anzahl direkter Verbindungen für Auth (standardmäßig 10)
- Max Request Duration: Maximal erlaubte Dauer einer Auth-Anfrage (standardmäßig 10s)
## Speicherung
## Storage
> [!TIP]
> Supabase ermöglicht **das Speichern von Dateien** und deren Zugriff über eine URL (es verwendet S3-Buckets).
> Supabase erlaubt **Dateien zu speichern** und über eine URL zugänglich zu machen (es verwendet S3-Buckets).
- Die Upload-Dateigrößenbeschränkung festlegen (standardmäßig 50 MB)
- Die S3-Verbindung wird mit einer URL wie folgt bereitgestellt: `https://jnanozjdybtpqgcwhdiz.supabase.co/storage/v1/s3`
- Es ist möglich, **S3-Zugriffsschlüssel** anzufordern, die aus einer `access key ID` (z. B. `a37d96544d82ba90057e0e06131d0a7b`) und einem `secret access key` (z. B. `58420818223133077c2cec6712a4f909aec93b4daeedae205aa8e30d5a860628`) bestehen
- Setze das Upload-Dateigrößenlimit (Standard ist 50MB)
- Die S3-Verbindung wird über eine URL angegeben wie: `https://jnanozjdybtpqgcwhdiz.supabase.co/storage/v1/s3`
- Es ist möglich, **S3 access key** anzufordern, die aus einer `access key ID` (z. B. `a37d96544d82ba90057e0e06131d0a7b`) und einer `secret access key` (z. B. `58420818223133077c2cec6712a4f909aec93b4daeedae205aa8e30d5a860628`) bestehen
## Edge-Funktionen
## Edge Functions
Es ist möglich, **Geheimnisse** in Supabase zu speichern, die auch **von Edge-Funktionen** zugänglich sind (sie können über das Web erstellt und gelöscht werden, aber es ist nicht möglich, ihren Wert direkt abzurufen).
Es ist auch möglich, **secrets zu speichern** in supabase, die dann von **edge functions** zugänglich sind (sie können über das Web erstellt und gelöscht werden, aber deren Werte können nicht direkt eingesehen werden).
## References
- [Building Hacker Communities: Bug Bounty Village, getDiscloseds Supabase Misconfig, and the LHE Squad (Ep. 133) YouTube](https://youtu.be/NI-eXMlXma4)
- [Critical Thinking Podcast Episode 133 page](https://www.criticalthinkingpodcast.io/episode-133-building-hacker-communities-bug-bounty-village-getdisclosed-and-the-lhe-squad/)
- [Supabase: Row Level Security (RLS)](https://supabase.com/docs/guides/auth/row-level-security)
- [PostgreSQL: Row Security Policies](https://www.postgresql.org/docs/current/ddl-rowsecurity.html)
- [PostgreSQL: CREATE VIEW (security_invoker, check option)](https://www.postgresql.org/docs/current/sql-createview.html)
- [PostgREST: OpenAPI documentation](https://postgrest.org/en/stable/references/api.html#openapi-documentation)
{{#include ../banners/hacktricks-training.md}}