Supabase Security
Reading time: 12 minutes
tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :
HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le đŹ groupe Discord ou le groupe telegram ou suivez-nous sur Twitter đŠ @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépÎts github.
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
<div class="mdbook-alerts mdbook-alerts-tip">
<p class="mdbook-alerts-title">
<span class="mdbook-alerts-icon"></span>
tip
</p>
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.
</div>
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é) :
<div class="codeblock_filename_container"><span class="codeblock_filename_inner hljs">bash</span></div>
```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 SECURITYest 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/DELETEsur 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 OPTIONet 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/authenticateddes 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
anonetauthenticated, 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
anonet pour un utilisateurauthenticatedminimal, 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'unsecret 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
- Building Hacker Communities: Bug Bounty Village, getDisclosedâs Supabase Misconfig, and the LHE Squad (Ep. 133) â YouTube
- Critical Thinking Podcast â Episode 133 page
- Supabase: Row Level Security (RLS)
- PostgreSQL: Row Security Policies
- PostgreSQL: CREATE VIEW (security_invoker, check option)
- PostgREST: OpenAPI documentation
tip
Apprenez et pratiquez le hacking AWS :
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP :
HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le đŹ groupe Discord ou le groupe telegram ou suivez-nous sur Twitter đŠ @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépÎts github.
HackTricks Cloud