mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-01-27 07:14:20 -08:00
Translated ['', 'src/pentesting-ci-cd/supabase-security.md'] to es
This commit is contained in:
@@ -2,9 +2,9 @@
|
||||
|
||||
{{#include ../banners/hacktricks-training.md}}
|
||||
|
||||
## Información Básica
|
||||
## Información básica
|
||||
|
||||
Según su [**página de inicio**](https://supabase.com/): Supabase es una alternativa de código abierto a Firebase. Comienza tu proyecto con una base de datos Postgres, Autenticación, APIs instantáneas, Funciones Edge, suscripciones en tiempo real, Almacenamiento y embeddings vectoriales.
|
||||
Según su [**landing page**](https://supabase.com/): Supabase es una alternativa de código abierto a Firebase. Comienza tu proyecto con una base de datos Postgres, Authentication, instant APIs, Edge Functions, Realtime subscriptions, Storage y Vector embeddings.
|
||||
|
||||
### Subdominio
|
||||
|
||||
@@ -13,33 +13,33 @@ Básicamente, cuando se crea un proyecto, el usuario recibirá un subdominio sup
|
||||
## **Configuración de la base de datos**
|
||||
|
||||
> [!TIP]
|
||||
> **Estos datos se pueden acceder desde un enlace como `https://supabase.com/dashboard/project/<project-id>/settings/database`**
|
||||
> **Estos datos pueden accederse desde un enlace como `https://supabase.com/dashboard/project/<project-id>/settings/database`**
|
||||
|
||||
Esta **base de datos** se desplegará en alguna región de AWS, y para conectarse a ella sería posible hacerlo conectándose a: `postgres://postgres.jnanozjdybtpqgcwhdiz:[YOUR-PASSWORD]@aws-0-us-west-1.pooler.supabase.com:5432/postgres` (esto fue creado en us-west-1).\
|
||||
La contraseña es una **contraseña que el usuario puso** previamente.
|
||||
Esta **base de datos** será desplegada en alguna región de AWS, y para conectarse a ella sería posible hacerlo conectándose a: `postgres://postgres.jnanozjdybtpqgcwhdiz:[YOUR-PASSWORD]@aws-0-us-west-1.pooler.supabase.com:5432/postgres` (este fue creado en us-west-1).
|
||||
La contraseña es una **contraseña que el usuario configuró** previamente.
|
||||
|
||||
Por lo tanto, dado que el subdominio es conocido y se usa como nombre de usuario y las regiones de AWS son limitadas, podría ser posible intentar **fuerza bruta la contraseña**.
|
||||
Por lo tanto, dado que el subdominio es conocido y se usa como nombre de usuario y las regiones de AWS son limitadas, podría ser posible intentar un **brute force** contra la contraseña.
|
||||
|
||||
Esta sección también contiene opciones para:
|
||||
|
||||
- Restablecer la contraseña de la base de datos
|
||||
- Configurar el agrupamiento de conexiones
|
||||
- Configurar connection pooling
|
||||
- Configurar SSL: Rechazar conexiones en texto plano (por defecto están habilitadas)
|
||||
- Configurar el tamaño del disco
|
||||
- Aplicar restricciones y prohibiciones de red
|
||||
- Configurar tamaño de disco
|
||||
- Aplicar restricciones y bloqueos de red
|
||||
|
||||
## Configuración de la API
|
||||
|
||||
> [!TIP]
|
||||
> **Estos datos se pueden acceder desde un enlace como `https://supabase.com/dashboard/project/<project-id>/settings/api`**
|
||||
> **Estos datos pueden accederse desde un enlace como `https://supabase.com/dashboard/project/<project-id>/settings/api`**
|
||||
|
||||
La URL para acceder a la API de supabase en tu proyecto será como: `https://jnanozjdybtpqgcwhdiz.supabase.co`.
|
||||
|
||||
### claves de api anon
|
||||
### anon API keys
|
||||
|
||||
También generará una **clave API anon** (`role: "anon"`), como: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImpuYW5vemRyb2J0cHFnY3doZGl6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTQ5OTI3MTksImV4cCI6MjAzMDU2ODcxOX0.sRN0iMGM5J741pXav7UxeChyqBE9_Z-T0tLA9Zehvqk` que la aplicación necesitará usar para contactar la clave API expuesta en nuestro ejemplo en
|
||||
También generará una **anon API key** (`role: "anon"`), como: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImpuYW5vemRyb2J0cHFnY3doZGl6Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTQ5OTI3MTksImV4cCI6MjAzMDU2ODcxOX0.sRN0iMGM5J741pXav7UxeChyqBE9_Z-T0tLA9Zehvqk` que la aplicación necesitará usar para contactar la API expuesta en nuestro ejemplo en
|
||||
|
||||
Es posible encontrar la API REST para contactar esta API en la [**docs**](https://supabase.com/docs/reference/self-hosting-auth/returns-the-configuration-settings-for-the-gotrue-server), pero los endpoints más interesantes serían:
|
||||
Es posible encontrar el API REST para contactar esta API en la [**docs**](https://supabase.com/docs/reference/self-hosting-auth/returns-the-configuration-settings-for-the-gotrue-server), pero los endpoints más interesantes serían:
|
||||
|
||||
<details>
|
||||
|
||||
@@ -72,7 +72,7 @@ Priority: u=1, i
|
||||
|
||||
<details>
|
||||
|
||||
<summary>Iniciar sesión (/auth/v1/token?grant_type=password)</summary>
|
||||
<summary>Login (/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>
|
||||
|
||||
Así que, cada vez que descubras un cliente que utiliza supabase con el subdominio que se le otorgó (es posible que un subdominio de la empresa tenga un CNAME sobre su subdominio de supabase), podrías intentar **crear una nueva cuenta en la plataforma utilizando la API de supabase**.
|
||||
Entonces, siempre que descubras un cliente que use supabase con el subdominio que se le asignó (es posible que un subdominio de la empresa tenga un CNAME apuntando sobre su subdominio de supabase), puedes intentar **crear una nueva cuenta en la plataforma usando la API de supabase**.
|
||||
|
||||
### claves api secretas / service_role
|
||||
### secret / service_role api keys
|
||||
|
||||
Una clave API secreta también se generará con **`role: "service_role"`**. Esta clave API debe ser secreta porque podrá eludir **Row Level Security**.
|
||||
También se generará una secret API key con **`role: "service_role"`**. Esta API key debe mantenerse en secreto porque podrá eludir **Row Level Security**.
|
||||
|
||||
La clave API se ve así: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImpuYW5vemRyb2J0cHFnY3doZGl6Iiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTcxNDk5MjcxOSwiZXhwIjoyMDMwNTY4NzE5fQ.0a8fHGp3N_GiPq0y0dwfs06ywd-zhTwsm486Tha7354`
|
||||
La API key se ve así: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImpuYW5vemRyb2J0cHFnY3doZGl6Iiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImlhdCI6MTcxNDk5MjcxOSwiZXhwIjoyMDMwNTY4NzE5fQ.0a8fHGp3N_GiPq0y0dwfs06ywd-zhTwsm486Tha7354`
|
||||
|
||||
### JWT Secret
|
||||
|
||||
Un **JWT Secret** también se generará para que la aplicación pueda **crear y firmar tokens JWT personalizados**.
|
||||
También se generará un **JWT Secret** para que la aplicación pueda **crear y firmar JWT tokens personalizados**.
|
||||
|
||||
## Autenticación
|
||||
|
||||
### Registros
|
||||
|
||||
> [!TIP]
|
||||
> Por **defecto**, supabase permitirá **que nuevos usuarios creen cuentas** en tu proyecto utilizando los endpoints de API mencionados anteriormente.
|
||||
> Por **defecto** supabase permitirá que **nuevos usuarios creen cuentas** en tu proyecto usando los endpoints de la API mencionados anteriormente.
|
||||
|
||||
Sin embargo, estas nuevas cuentas, por defecto, **necesitarán validar su dirección de correo electrónico** para poder iniciar sesión en la cuenta. Es posible habilitar **"Permitir inicios de sesión anónimos"** para permitir que las personas inicien sesión sin verificar su dirección de correo electrónico. Esto podría otorgar acceso a **datos inesperados** (obtienen los roles `public` y `authenticated`).\
|
||||
Esta es una muy mala idea porque supabase cobra por usuario activo, así que las personas podrían crear usuarios e iniciar sesión y supabase cobrará por esos:
|
||||
Sin embargo, por defecto estas cuentas nuevas **necesitarán validar su dirección de email** para poder iniciar sesión en la cuenta. Es posible habilitar **"Allow anonymous sign-ins"** para permitir que la gente inicie sesión sin verificar su correo. Esto podría otorgar acceso a **datos inesperados** (obtienen los roles `public` y `authenticated`).\
|
||||
Esto es una muy mala idea porque supabase cobra por usuario activo, así que la gente podría crear usuarios e iniciar sesión y supabase cobrará por ellos:
|
||||
|
||||
<figure><img src="../images/image (1) (1) (1) (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
### Contraseñas y sesiones
|
||||
#### Auth: Imposición del registro en el servidor
|
||||
|
||||
Es posible indicar la longitud mínima de la contraseña (por defecto), requisitos (no por defecto) y deshabilitar el uso de contraseñas filtradas.\
|
||||
Ocultar el botón de registro en el frontend no es suficiente. Si el **Auth server aún permite registros**, un atacante puede llamar a la API directamente con la `anon` key pública y crear usuarios arbitrarios.
|
||||
|
||||
Prueba rápida (desde un cliente no autenticado):
|
||||
```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
|
||||
```
|
||||
Endurecimiento esperado:
|
||||
- Deshabilitar registros por email/contraseña en el Dashboard: Authentication → Providers → Email → Disable sign ups (invite-only), o configurar el ajuste equivalente de GoTrue.
|
||||
- Verificar que la API ahora devuelve 4xx para la llamada previa y que no se crea ningún usuario nuevo.
|
||||
- Si dependes de invites o SSO, asegúrate de que todos los demás providers estén deshabilitados a menos que sean necesarios explícitamente.
|
||||
|
||||
## RLS y Views: Bypass de escritura vía PostgREST
|
||||
|
||||
Usar una Postgres VIEW para “ocultar” columnas sensibles y exponerla vía PostgREST puede cambiar cómo se evalúan los privilegios. En PostgreSQL:
|
||||
- Las vistas ordinarias se ejecutan con los privilegios del propietario de la view por defecto (definer semantics). En PG ≥15 puedes optar por `security_invoker`.
|
||||
- Row Level Security (RLS) se aplica en las tablas base. Los propietarios de la tabla evitan RLS a menos que `FORCE ROW LEVEL SECURITY` esté establecido en la tabla.
|
||||
- Las updatable views pueden aceptar INSERT/UPDATE/DELETE que luego se aplican a la tabla base. Sin `WITH CHECK OPTION`, las escrituras que no coinciden con el predicado de la view pueden seguir teniendo éxito.
|
||||
|
||||
Patrón de riesgo observado en entornos reales:
|
||||
- Una view con columnas reducidas se expone vía Supabase REST y se concede a `anon`/`authenticated`.
|
||||
- PostgREST permite DML sobre la updatable view y la operación se evalúa con los privilegios del propietario de la view, eludiendo efectivamente las políticas RLS previstas en la tabla base.
|
||||
- Resultado: clientes con bajos privilegios pueden editar masivamente filas (ej., bios/avatars de perfil) que no deberían poder modificar.
|
||||
|
||||
Ejemplo de escritura vía view (intentado desde un cliente público):
|
||||
```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>"
|
||||
```
|
||||
Lista de verificación de endurecimiento para views y RLS:
|
||||
- Prefiere exponer las tablas base con permisos explícitos de mínimo privilegio y políticas RLS precisas.
|
||||
- Si debes exponer una view:
|
||||
- Hazla no actualizable (p. ej., incluye expresiones/joins) o deniega `INSERT/UPDATE/DELETE` en la view a todos los roles no confiables.
|
||||
- Aplica `ALTER VIEW <v> SET (security_invoker = on)` para que se usen los privilegios del invocador en lugar de los del propietario.
|
||||
- En las tablas base, usa `ALTER TABLE <t> FORCE ROW LEVEL SECURITY;` para que incluso los propietarios estén sujetos a RLS.
|
||||
- Si permites escrituras vía una view actualizable, añade `WITH [LOCAL|CASCADED] CHECK OPTION` y políticas RLS complementarias en las tablas base para asegurar que solo las filas permitidas puedan ser escritas/modificadas.
|
||||
- En Supabase, evita otorgar a `anon`/`authenticated` privilegios de escritura en views a menos que hayas verificado el comportamiento end-to-end con pruebas.
|
||||
|
||||
Detection tip:
|
||||
- Desde `anon` y un usuario de prueba `authenticated`, intenta todas las operaciones CRUD contra cada tabla/view expuesta. Cualquier escritura exitosa donde esperabas negación indica una mala configuración.
|
||||
|
||||
### Exploración CRUD impulsada por OpenAPI desde roles anon/auth
|
||||
|
||||
PostgREST expone un documento OpenAPI que puedes usar para enumerar todos los recursos REST y luego sondear automáticamente las operaciones permitidas desde roles de bajo privilegio.
|
||||
|
||||
Obtén el OpenAPI (funciona con la anon key pública):
|
||||
```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[]'
|
||||
```
|
||||
Patrón de sondeo (ejemplos):
|
||||
- Leer una sola fila (esperar 401/403/200 dependiendo de 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>"
|
||||
```
|
||||
- Comprobar que UPDATE está bloqueado (usa un filtro inexistente para evitar alterar datos durante las pruebas):
|
||||
```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"
|
||||
```
|
||||
- Comprobar que INSERT está bloqueado:
|
||||
```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>"
|
||||
```
|
||||
- Probar que DELETE está bloqueado:
|
||||
```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"
|
||||
```
|
||||
Recomendaciones:
|
||||
- Automatiza las pruebas anteriores para ambos `anon` y un usuario mínimamente `authenticated` e intégralas en CI para detectar regresiones.
|
||||
- Trata cada table/view/function expuesto como una superficie de primera clase. No asumas que una view “hereda” la misma postura RLS que sus tablas base.
|
||||
|
||||
### Contraseñas & sesiones
|
||||
|
||||
Es posible indicar la longitud mínima de contraseña (por defecto), requisitos (no hay por defecto) y prohibir el uso de leaked passwords.\
|
||||
Se recomienda **mejorar los requisitos ya que los predeterminados son débiles**.
|
||||
|
||||
- Sesiones de Usuario: Es posible configurar cómo funcionan las sesiones de usuario (tiempos de espera, 1 sesión por usuario...)
|
||||
- Protección contra Bots y Abusos: Es posible habilitar Captcha.
|
||||
- User Sessions: Es posible configurar cómo funcionan las sesiones de usuario (tiempos de expiración, 1 sesión por usuario...)
|
||||
- Bot and Abuse Protection: Es posible habilitar Captcha.
|
||||
|
||||
### Configuraciones SMTP
|
||||
### SMTP Settings
|
||||
|
||||
Es posible establecer un SMTP para enviar correos electrónicos.
|
||||
Es posible configurar un SMTP para enviar correos.
|
||||
|
||||
### Configuraciones Avanzadas
|
||||
### Advanced Settings
|
||||
|
||||
- Establecer tiempo de expiración para tokens de acceso (3600 por defecto)
|
||||
- Establecer para detectar y revocar tokens de actualización potencialmente comprometidos y tiempo de espera
|
||||
- MFA: Indicar cuántos factores MFA pueden ser inscritos a la vez por usuario (10 por defecto)
|
||||
- Máx. Conexiones Directas a la Base de Datos: Número máximo de conexiones utilizadas para autenticación (10 por defecto)
|
||||
- Duración Máxima de Solicitud: Tiempo máximo permitido para que dure una solicitud de autenticación (10s por defecto)
|
||||
- Establecer tiempo de expiración para los access tokens (3600 por defecto)
|
||||
- Detectar y revocar refresh tokens potencialmente comprometidos y caducarlos
|
||||
- MFA: Indicar cuántos factores MFA pueden registrarse simultáneamente por usuario (10 por defecto)
|
||||
- Max Direct Database Connections: Número máximo de conexiones usadas para auth (10 por defecto)
|
||||
- Max Request Duration: Tiempo máximo permitido para que dure una solicitud de Auth (10s por defecto)
|
||||
|
||||
## Almacenamiento
|
||||
## Storage
|
||||
|
||||
> [!TIP]
|
||||
> Supabase permite **almacenar archivos** y hacerlos accesibles a través de una URL (utiliza buckets S3).
|
||||
> Supabase permite **almacenar archivos** y hacerlos accesibles mediante una URL (usa S3 buckets).
|
||||
|
||||
- Establecer el límite de tamaño de archivo de carga (el predeterminado es 50MB)
|
||||
- La conexión S3 se da con una URL como: `https://jnanozjdybtpqgcwhdiz.supabase.co/storage/v1/s3`
|
||||
- Es posible **solicitar una clave de acceso S3** que se forma por un `access key ID` (por ejemplo, `a37d96544d82ba90057e0e06131d0a7b`) y una `secret access key` (por ejemplo, `58420818223133077c2cec6712a4f909aec93b4daeedae205aa8e30d5a860628`)
|
||||
- Establecer el límite de tamaño de archivo para uploads (por defecto es 50MB)
|
||||
- La conexión S3 se proporciona con una URL como: `https://jnanozjdybtpqgcwhdiz.supabase.co/storage/v1/s3`
|
||||
- Es posible **solicitar S3 access key** que se forman por un `access key ID` (p. ej. `a37d96544d82ba90057e0e06131d0a7b`) y un `secret access key` (p. ej. `58420818223133077c2cec6712a4f909aec93b4daeedae205aa8e30d5a860628`)
|
||||
|
||||
## Funciones Edge
|
||||
## Edge Functions
|
||||
|
||||
Es posible **almacenar secretos** en supabase también, que serán **accesibles por funciones edge** (se pueden crear y eliminar desde la web, pero no es posible acceder a su valor directamente).
|
||||
Es posible **almacenar secrets** en supabase también, los cuales serán **accesibles por edge functions** (pueden crearse y eliminarse desde la web, pero no es posible acceder directamente a su valor).
|
||||
|
||||
## 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}}
|
||||
|
||||
Reference in New Issue
Block a user