Supabase Security

Tip

Apprenez & pratiquez AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Apprenez & pratiquez GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Apprenez & pratiquez Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Soutenez HackTricks

Informations de base

Comme indiqué sur leur landing page : Supabase est une alternative open source à Firebase. Démarrez votre projet avec une base de données Postgres, Authentication, instant APIs, Edge Functions, Realtime subscriptions, Storage, and Vector embeddings.

Sous-domaine

Lorsque un projet est créé, l’utilisateur reçoit gĂ©nĂ©ralement un sous-domaine supabase.co tel que : jnanozjdybtpqgcwhdiz.supabase.co

Configuration de la base de données

Tip

This data can be accessed from a link like https://supabase.com/dashboard/project/<project-id>/settings/database

Cette database sera dĂ©ployĂ©e dans une rĂ©gion AWS, et pour s’y connecter il est possible de le faire en se connectant Ă  : postgres://postgres.jnanozjdybtpqgcwhdiz:[YOUR-PASSWORD]@aws-0-us-west-1.pooler.supabase.com:5432/postgres (cela a Ă©tĂ© créé en us-west-1).
Le mot de passe est le mot de passe choisi prĂ©cĂ©demment par l’utilisateur.

Ainsi, comme le sous-domaine est connu et qu’il est utilisĂ© comme nom d’utilisateur et que les rĂ©gions AWS sont limitĂ©es, il pourrait ĂȘtre possible d’essayer de brute force the password.

Cette section contient aussi des options pour :

  • Reset the database password
  • Configure connection pooling
  • Configure SSL: Reject plan-text connections (by default they are enabled)
  • Configure Disk size
  • Apply network restrictions and bans

Configuration de l’API

Tip

This data can be accessed from a link like https://supabase.com/dashboard/project/<project-id>/settings/api

L’URL pour accĂ©der Ă  l’API Supabase de votre projet ressemblera Ă  : https://jnanozjdybtpqgcwhdiz.supabase.co.

anon api keys

Elle gĂ©nĂ©rera aussi une anon API key (role: "anon"), comme : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImpuYW5vemRyb2J0cHFnY3doZGl6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTQ5OTI3MTksImV4cCI6MjAzMDU2ODcxOX0.sRN0iMGM5J741pXav7UxeChyqBE9_Z-T0tLA9Zehvqk que l’application devra utiliser pour contacter l’API exposĂ©e dans notre exemple dans

Il est possible de trouver l’API REST pour contacter cette API dans les docs, mais les endpoints les plus intĂ©ressants seraient :

Signup (/auth/v1/signup) ``` POST /auth/v1/signup HTTP/2 Host: id.io.net Content-Length: 90 X-Client-Info: supabase-js-web/2.39.2 Sec-Ch-Ua: "Not-A.Brand";v="99", "Chromium";v="124" Sec-Ch-Ua-Mobile: ?0 Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImpuYW5vemRyb2J0cHFnY3doZGl6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTQ5OTI3MTksImV4cCI6MjAzMDU2ODcxOX0.sRN0iMGM5J741pXav7UxeChyqBE9_Z-T0tLA9Zehvqk User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.60 Safari/537.36 Content-Type: application/json;charset=UTF-8 Apikey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImpuYW5vemRyb2J0cHFnY3doZGl6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTQ5OTI3MTksImV4cCI6MjAzMDU2ODcxOX0.sRN0iMGM5J741pXav7UxeChyqBE9_Z-T0tLA9Zehvqk Sec-Ch-Ua-Platform: "macOS" Accept: */* Origin: https://cloud.io.net Sec-Fetch-Site: same-site Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Referer: https://cloud.io.net/ Accept-Encoding: gzip, deflate, br Accept-Language: en-GB,en-US;q=0.9,en;q=0.8 Priority: u=1, i

{“email”:“test@exmaple.com”,“password”:“SomeCOmplexPwd239.”}

</details>

<details>

<summary>Connexion (/auth/v1/token?grant_type=password)</summary>

POST /auth/v1/token?grant_type=password HTTP/2 Host: hypzbtgspjkludjcnjxl.supabase.co Content-Length: 80 X-Client-Info: supabase-js-web/2.39.2 Sec-Ch-Ua: “Not-A.Brand”;v=“99”, “Chromium”;v=“124” Sec-Ch-Ua-Mobile: ?0 Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImpuYW5vemRyb2J0cHFnY3doZGl6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTQ5OTI3MTksImV4cCI6MjAzMDU2ODcxOX0.sRN0iMGM5J741pXav7UxeChyqBE9_Z-T0tLA9Zehvqk User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.6367.60 Safari/537.36 Content-Type: application/json;charset=UTF-8 Apikey: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImpuYW5vemRyb2J0cHFnY3doZGl6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTQ5OTI3MTksImV4cCI6MjAzMDU2ODcxOX0.sRN0iMGM5J741pXav7UxeChyqBE9_Z-T0tLA9Zehvqk Sec-Ch-Ua-Platform: “macOS” Accept: / Origin: https://cloud.io.net Sec-Fetch-Site: same-site Sec-Fetch-Mode: cors Sec-Fetch-Dest: empty Referer: https://cloud.io.net/ Accept-Encoding: gzip, deflate, br Accept-Language: en-GB,en-US;q=0.9,en;q=0.8 Priority: u=1, i

{“email”:“test@exmaple.com”,“password”:“SomeCOmplexPwd239.”}

</details>

Ainsi, chaque fois que vous découvrez un client utilisant supabase avec le sous-domaine qui lui a été attribué (il est possible qu'un sous-domaine de l'entreprise ait un CNAME pointant vers leur sous-domaine supabase), vous pouvez essayer de **créer un nouveau compte sur la plateforme en utilisant l'API supabase**.

### Clés API secret / service_role

Une clé API secrÚte sera également générée avec **`role: "service_role"`**. Cette clé API doit rester secrÚte car elle pourra contourner la **Row Level Security**.

La clé API ressemble à ceci : `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImpuYW5vemRyb2J0cHFnY3doZGl6Iiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTcxNDk5MjcxOSwiZXhwIjoyMDMwNTY4NzE5fQ.0a8fHGp3N_GiPq0y0dwfs06ywd-zhTwsm486Tha7354`

### JWT Secret

Un **JWT Secret** sera également généré afin que l'application puisse **créer et signer des tokens JWT personnalisés**.

## Authentification

### Inscription

> [!TIP]
> Par **défaut** supabase permettra aux **nouveaux utilisateurs de créer des comptes** sur votre projet en utilisant les endpoints API mentionnés précédemment.

Cependant, ces nouveaux comptes, par dĂ©faut, **devront valider leur adresse e‑mail** pour pouvoir se connecter au compte. Il est possible d'activer **"Allow anonymous sign-ins"** pour permettre aux personnes de se connecter sans vĂ©rifier leur adresse e‑mail. Cela pourrait donner accĂšs Ă  des **donnĂ©es inattendues** (ils obtiennent les rĂŽles `public` et `authenticated`).\
C'est une trÚs mauvaise idée car supabase facture par utilisateur actif, donc des personnes pourraient créer des utilisateurs, se connecter et supabase facturera pour ceux-ci :

<figure><img src="../images/image (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>

#### Auth : application cÎté serveur des restrictions d'inscription

Masquer le bouton d'inscription dans le frontend ne suffit pas. Si le **serveur Auth autorise toujours les inscriptions**, un attaquant peut appeler l'API directement avec la clé publique `anon` et créer des utilisateurs arbitraires.

Test rapide (depuis un client non authentifié) :
```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

Durcissement attendu :

  • DĂ©sactiver les inscriptions par email/mot de passe dans le Dashboard : Authentication → Providers → Email → Disable sign ups (invite-only), ou dĂ©finir l’équivalent dans GoTrue.
  • VĂ©rifier que l’API renvoie maintenant un code 4xx Ă  l’appel prĂ©cĂ©dent et qu’aucun nouvel utilisateur n’est créé.
  • Si vous dĂ©pendez des invitations ou du SSO, assurez-vous que tous les autres providers sont dĂ©sactivĂ©s sauf si explicitement nĂ©cessaires.

RLS and Views: contournement d’écriture via PostgREST

Utiliser une Postgres VIEW pour « masquer » des colonnes sensibles et l’exposer via PostgREST peut modifier la façon dont les privilĂšges sont Ă©valuĂ©s. Dans PostgreSQL :

  • Les vues ordinaires s’exĂ©cutent par dĂ©faut avec les privilĂšges du propriĂ©taire de la view (definer semantics). Dans PG ≄15, vous pouvez opter pour security_invoker.
  • Row Level Security (RLS) s’applique aux tables de base. Les propriĂ©taires des tables contournent le RLS sauf si FORCE ROW LEVEL SECURITY est dĂ©fini sur la table.
  • Les vues updatables peuvent accepter des INSERT/UPDATE/DELETE qui sont ensuite appliquĂ©s Ă  la table de base. Sans WITH CHECK OPTION, des Ă©critures qui ne correspondent pas au prĂ©dicat de la view peuvent tout de mĂȘme rĂ©ussir.

Schéma de risque observé sur le terrain :

  • Une view ne contenant que certaines colonnes est exposĂ©e via Supabase REST et son accĂšs est accordĂ© Ă  anon/authenticated.
  • PostgREST autorise du DML sur la view updatable et l’opĂ©ration est Ă©valuĂ©e avec les privilĂšges du propriĂ©taire de la view, contournant de fait les politiques RLS prĂ©vues sur la table de base.
  • ConsĂ©quence : des clients Ă  faible privilĂšge peuvent modifier en masse des lignes (p. ex. bios/avatars de profil) qu’ils ne devraient pas pouvoir modifier.

Écriture illustrative via la view (tentative depuis un client public) :

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>"

Checklist de durcissement pour les vues et RLS :

  • PrivilĂ©giez l’exposition des tables de base avec des autorisations explicites au moindre privilĂšge et des politiques RLS prĂ©cises.
  • Si vous devez exposer une vue :
  • Rendez-la non modifiable (par ex., inclure des expressions/jointures) ou refusez INSERT/UPDATE/DELETE sur la vue pour tous les rĂŽles non fiables.
  • Appliquez ALTER VIEW <v> SET (security_invoker = on) pour que les privilĂšges de l’invocateur soient utilisĂ©s au lieu de ceux du propriĂ©taire.
  • Sur les tables de base, utilisez ALTER TABLE <t> FORCE ROW LEVEL SECURITY; afin que mĂȘme les propriĂ©taires soient soumis au RLS.
  • Si vous autorisez des Ă©critures via une vue modifiable, ajoutez WITH [LOCAL|CASCADED] CHECK OPTION et des RLS complĂ©mentaires sur les tables de base pour garantir que seules les lignes autorisĂ©es peuvent ĂȘtre Ă©crites/modifiĂ©es.
  • Dans Supabase, Ă©vitez d’accorder Ă  anon/authenticated des privilĂšges d’écriture sur les vues sauf si vous avez vĂ©rifiĂ© le comportement end-to-end avec des tests.

Detection tip:

  • Depuis un user de test anon et authenticated, tentez toutes les opĂ©rations CRUD sur chaque table/vue exposĂ©e. Toute Ă©criture rĂ©ussie alors que vous attendiez un refus indique une mauvaise configuration.

Sondage CRUD piloté par OpenAPI depuis les rÎles anon/auth

PostgREST expose un document OpenAPI que vous pouvez utiliser pour énumérer toutes les ressources REST, puis sonder automatiquement les opérations autorisées depuis des rÎles peu privilégiés.

RĂ©cupĂ©rez l’OpenAPI (fonctionne avec la clĂ© publique anon) :

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[]'

ModĂšle de sonde (exemples):

  • Lire une seule ligne (s’attendre Ă  401/403/200 selon RLS):
curl -s "https://<PROJECT_REF>.supabase.co/rest/v1/<table>?select=*&limit=1" \
-H "apikey: <SUPABASE_ANON_KEY>" \
-H "Authorization: Bearer <SUPABASE_ANON_KEY>"
  • Tester que UPDATE est bloquĂ© (utilisez un filtre inexistant pour Ă©viter d’altĂ©rer les donnĂ©es pendant les tests) :
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"
  • VĂ©rifier si INSERT est bloquĂ© :
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>"
  • VĂ©rifier que DELETE est bloquĂ©:
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"

Recommandations:

  • Automatisez les tests prĂ©cĂ©dents pour anon et pour un utilisateur authenticated minimal, et intĂ©grez-les dans la CI pour dĂ©tecter les rĂ©gressions.
  • Traitez chaque table/vue/fonction exposĂ©e comme une surface de premier ordre. Ne supposez pas qu’une vue “hĂ©rite” de la mĂȘme posture RLS que ses tables de base.

Mots de passe & sessions

Il est possible d’indiquer la longueur minimale du mot de passe (par dĂ©faut), les exigences (aucune par dĂ©faut) et d’interdire l’utilisation de leaked passwords.
Il est recommandĂ© d’amĂ©liorer les exigences car celles par dĂ©faut sont faibles.

  • Sessions utilisateur : Il est possible de configurer le fonctionnement des sessions (timeouts, 1 session par utilisateur
)
  • Protection contre les bots et les abus : Il est possible d’activer Captcha.

ParamĂštres SMTP

Il est possible de configurer un serveur SMTP pour envoyer des e-mails.

ParamÚtres avancés

  • DĂ©finir le temps d’expiration des access tokens (3600 par dĂ©faut)
  • Activer la dĂ©tection et la rĂ©vocation des refresh tokens potentiellement compromis ainsi que le timeout
  • MFA : Indiquer combien de facteurs MFA peuvent ĂȘtre enregistrĂ©s simultanĂ©ment par utilisateur (10 par dĂ©faut)
  • Max Direct Database Connections : Nombre maximal de connexions utilisĂ©es pour l’auth (10 par dĂ©faut)
  • Max Request Duration : DurĂ©e maximale autorisĂ©e pour une requĂȘte Auth (10s par dĂ©faut)

Stockage

Tip

Supabase permet de stocker des fichiers et de les rendre accessibles via une URL (il utilise S3 buckets).

  • DĂ©finir la taille maximale d’upload (par dĂ©faut 50MB)
  • La connexion S3 est fournie avec une URL comme : https://jnanozjdybtpqgcwhdiz.supabase.co/storage/v1/s3
  • Il est possible de demander des S3 access key qui sont composĂ©es d’un access key ID (ex. a37d96544d82ba90057e0e06131d0a7b) et d’un secret access key (ex. 58420818223133077c2cec6712a4f909aec93b4daeedae205aa8e30d5a860628)

Edge Functions

Il est aussi possible de stocker des secrets dans supabase qui seront accessibles by edge functions (ils peuvent ĂȘtre créés et supprimĂ©s depuis le web, mais il n’est pas possible d’accĂ©der directement Ă  leur valeur).

References

Tip

Apprenez & pratiquez AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Apprenez & pratiquez GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Apprenez & pratiquez Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Soutenez HackTricks