From e49d038938beeb4989fcc6c03704e942bbc9c239 Mon Sep 17 00:00:00 2001 From: Translator Date: Mon, 29 Sep 2025 23:06:18 +0000 Subject: [PATCH] Translated ['', 'src/pentesting-ci-cd/supabase-security.md'] to de --- src/pentesting-ci-cd/supabase-security.md | 200 +++++++++++++++++----- 1 file changed, 155 insertions(+), 45 deletions(-) diff --git a/src/pentesting-ci-cd/supabase-security.md b/src/pentesting-ci-cd/supabase-security.md index baa45d79d..933bbf267 100644 --- a/src/pentesting-ci-cd/supabase-security.md +++ b/src/pentesting-ci-cd/supabase-security.md @@ -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//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//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:
@@ -72,7 +72,7 @@ Priority: u=1, i
-Login (/auth/v1/token?grant_type=password) +Anmeldung (/auth/v1/token?grant_type=password) ``` POST /auth/v1/token?grant_type=password HTTP/2 Host: hypzbtgspjkludjcnjxl.supabase.co @@ -99,61 +99,171 @@ Priority: u=1, i ```
-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:
+#### 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: " \ +-H "Authorization: Bearer " \ +-H "Content-Type: application/json" \ +-d '{"email":"attacker@example.com","password":"Sup3rStr0ng!"}' \ +https://.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: " \ +-H "Authorization: Bearer " \ +-H "Content-Type: application/json" \ +-H "Prefer: return=representation" \ +-d '{"bio":"pwned","avatar_url":"https://i.example/pwn.png"}' \ +"https://.supabase.co/rest/v1/users_view?id=eq." +``` +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 SET (security_invoker = on)`, sodass die Rechte des Invokers statt der des Owners verwendet werden. +- Auf Basistabellen verwende `ALTER TABLE 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://.supabase.co/rest/v1/ \ +-H "apikey: " \ +-H "Authorization: Bearer " \ +-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://.supabase.co/rest/v1/?select=*&limit=1" \ +-H "apikey: " \ +-H "Authorization: Bearer " +``` +- 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: " \ +-H "Authorization: Bearer " \ +-H "Content-Type: application/json" \ +-H "Prefer: return=minimal" \ +-d '{"__probe":true}' \ +"https://.supabase.co/rest/v1/?id=eq.00000000-0000-0000-0000-000000000000" +``` +- Test INSERT ist blockiert: +```bash +curl -i -X POST \ +-H "apikey: " \ +-H "Authorization: Bearer " \ +-H "Content-Type: application/json" \ +-H "Prefer: return=minimal" \ +-d '{"__probe":true}' \ +"https://.supabase.co/rest/v1/" +``` +- Prüfen, ob DELETE blockiert ist: +```bash +curl -i -X DELETE \ +-H "apikey: " \ +-H "Authorization: Bearer " \ +"https://.supabase.co/rest/v1/?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, getDisclosed’s 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}}