Auditoría de Firebase para SaaS y startups: checklist en 15 puntos (2026)

Tu Firebase ya está en producción. Checklist de 15 puntos: qué auditar, con qué comando y cómo priorizar el fix. Pensado para CTOs y leads, no para reescribir la app.

K
Kevin Reyes
15 min de lectura

Tienes Firebase en producción desde hace meses. Quizá años. Han pasado tres rondas de features, cuatro desarrolladores han tocado las reglas, y nadie recuerda con seguridad si aquel if true que se metió "para una migración" sigue ahí. Ahora un cliente B2B te pide evidencias de seguridad, o un inversor pregunta por tu postura, o simplemente te has dado cuenta de que la última vez que alguien revisó las reglas era distinto el equipo entero.

No vas a reescribir la aplicación. No vas a migrar a otro backend. Necesitas saber, hoy, qué está mal en tu Firebase y qué hay que tocar primero. Eso es una auditoría, no una guía de configuración inicial.

Este checklist está diseñado para eso. 15 controles agrupados en 5 categorías, ejecutables en menos de 30 minutos cada uno, con el comando exacto, la pinta que tiene un resultado vulnerable y la prioridad del fix. Pensado para CTOs, lead engineers y DevSecOps de SaaS y startups que ya han pasado la fase "tutorial de Firebase" y necesitan profesionalizar la postura sin frenar el roadmap.

Si todavía estás eligiendo reglas iniciales o quieres entender los errores de configuración por dónde empieza casi todo el mundo, lee primero los 7 errores más frecuentes de configuración Firebase, los 10 errores de Security Rules en startups y por qué Firebase filtra datos por defecto. Este post es la siguiente parada: cómo verificar que lo que ya tienes desplegado no está roto.


Cómo usar este checklist

Cada control tiene cuatro piezas:

  1. Qué comprobar — el chequeo concreto, en lenguaje claro.
  2. Cómo hacerlo — comando exacto (firebase, gcloud, gsutil, curl) o ruta en la consola.
  3. Pinta vulnerable — cómo se ve un resultado que indica problema.
  4. Prioridad del fix — crítico, alto o medio. Atiéndelos en ese orden.

Antes de empezar, asegúrate de tener instalada la Firebase CLI (npm install -g firebase-tools), la Google Cloud CLI (gcloud) y autenticación válida contra el proyecto:

Setup mínimo antes de auditar
1# Login y selección de proyecto
2firebase login
3gcloud auth login
4gcloud config set project TU_PROJECT_ID
5firebase use TU_PROJECT_ID
6
7# Descarga el estado actual de las reglas a local
8firebase firestore:rules > /tmp/firestore.rules.actual
9curl -s "https://TU_PROJECT_ID-default-rtdb.europe-west1.firebasedatabase.app/.json?shallow=true&access_token=$(gcloud auth print-access-token)" > /tmp/rtdb-shallow.json
10gsutil ls -L -b gs://TU_PROJECT_ID.appspot.com 2>/dev/null

Con eso ya puedes auditar sin tocar producción. Todo lo que sigue es lectura — no modifica nada.


Categoría 1: Auth y reglas

Check 1 — ¿Hay reglas en producción que se hayan generado en "modo test"?

Qué comprobar. Las reglas que la consola de Firebase genera para arrancar incluyen cláusulas tipo if request.time < timestamp.date(...) con caducidad a 30 días. Si ese día llegó y nadie revisó, las reglas pueden haber quedado en un estado intermedio inseguro.

Cómo hacerlo. Inspecciona el contenido descargado en el setup:

Buscar el patrón típico de modo test
1grep -E "request\.time|timestamp\.date|if true" /tmp/firestore.rules.actual

Pinta vulnerable. Cualquier if true, o un request.time < timestamp.date(YYYY, MM, DD) con fecha pasada que actúa como if true efectivo. También sospecha de bloques con comentarios como // TODO: cambiar antes de producción.

Prioridad: crítica. Si encuentras esto, el resto del checklist puede esperar 24 horas. Esto no.

Check 2 — ¿Hay alguna regla allow read|write: if request.auth != null global?

Qué comprobar. El patrón "requiere autenticación" sin scoping por UID es uno de los hallazgos más frecuentes. Cualquier usuario que se registre puede leer toda la base.

Cómo hacerlo.

Detectar reglas auth-only sin scoping
1grep -nE "allow (read|write|read,\s*write).*request\.auth\s*!=\s*null" /tmp/firestore.rules.actual | grep -v "request\.auth\.uid"

Pinta vulnerable. Líneas tipo allow read: if request.auth != null; sin un && request.auth.uid == ... o && resource.data.ownerId == request.auth.uid después.

Prioridad: crítica si tu app permite registro abierto. Alta si el registro está controlado.

Check 3 — ¿La verificación de email está exigida en las reglas para acciones sensibles?

Qué comprobar. Firebase Auth crea cuentas sin verificar el email salvo que tú obligues. Si tus reglas no usan request.auth.token.email_verified, cualquiera con un email desechable accede.

Cómo hacerlo.

Buscar uso de email_verified en reglas
1grep -n "email_verified" /tmp/firestore.rules.actual || echo "FALTA: ninguna regla usa email_verified"

Pinta vulnerable. Cero ocurrencias en un proyecto que tiene login con email/password activo.

Prioridad: alta. No es crítico si tus datos no son sensibles, pero sí si manejas pagos, datos personales o cualquier cosa con implicaciones GDPR.

Check 4 — ¿Hay custom claims definidos para roles, o todo se decide leyendo Firestore?

Qué comprobar. Si tus reglas hacen get(/databases/.../users/$(auth.uid)).data.role == 'admin', cada lectura paga una lectura extra y, peor, el rol vive en una colección que un atacante puede intentar modificar.

Cómo hacerlo.

Buscar lecturas dentro de reglas
1grep -nE "get\(/databases/" /tmp/firestore.rules.actual

Y para verificar qué claims tiene un usuario concreto:

Inspeccionar custom claims con la Admin SDK
1// node script
2const admin = require('firebase-admin');
3admin.initializeApp({ credential: admin.credential.applicationDefault() });
4
5admin.auth().getUserByEmail('[email protected]')
6.then(user => console.log(user.customClaims || 'sin claims'));

Pinta vulnerable. Reglas que hacen get() repetidos para decidir privilegios y/o ningún custom claim definido en cuentas que sí tienen rol especial.

Prioridad: media (impacto en factura y mantenibilidad), alta si los datos auxiliares que se leen son modificables por el usuario.


Categoría 2: Firestore y Realtime Database

Check 5 — ¿La Realtime Database responde sin token a /.json?

Qué comprobar. El test definitivo para Realtime Database. Si responde con datos a un curl sin auth, cualquier persona en internet puede descargar tu base entera.

Cómo hacerlo.

Probar el endpoint REST sin autenticación
1# Reemplaza la URL por la de tu instancia
2curl -sS "https://TU_PROJECT_ID-default-rtdb.europe-west1.firebasedatabase.app/.json?shallow=true" | head -c 500

Pinta vulnerable. Cualquier respuesta que no sea {"error":"Permission denied"} o null. Si ves nombres de colecciones o datos, estás expuesto al mundo.

Prioridad: crítica. Es el equivalente a publicar un dump de tu base de datos.

Check 6 — ¿Las reglas de Firestore validan request.resource.data en escrituras?

Qué comprobar. Sin validación de schema en reglas, un cliente puede crear documentos con campos arbitrarios. Es la puerta de entrada a BOLA en clientes Firestore: un atacante mete un role: "admin" o un total: -100 en el body y te lo aceptas.

Cómo hacerlo.

Verificar uso de hasOnly / hasAll en reglas
1grep -nE "hasOnly|hasAll|request\.resource\.data" /tmp/firestore.rules.actual | wc -l

Pinta vulnerable. Un proyecto con varias colecciones y cero ocurrencias de hasOnly/hasAll casi seguro acepta payloads arbitrarios. Revisa al menos las colecciones que contienen role, plan, credits, balance, isAdmin, permissions.

Prioridad: alta. En SaaS multi-tenant es crítica.

Check 7 — ¿Hay reglas con match /{document=**} que sobrescriben reglas más específicas?

Qué comprobar. El comodín recursivo {document=**} aplica a todo el subárbol. Una regla permisiva en path superior anula reglas restrictivas más abajo. Es el error conceptual más común en Firestore.

Cómo hacerlo.

Listar todas las reglas con comodín recursivo
1grep -nE "\{document=\*\*\}|\{[a-zA-Z]+=\*\*\}" /tmp/firestore.rules.actual

Pinta vulnerable. Cualquier match /{document=**} con un allow permisivo en un nivel alto del árbol. El test mental: ¿esta regla aplica también a colecciones que añadiré dentro de seis meses sin acordarme de revisar esto? Si la respuesta es sí y permite algo más que if false, hay riesgo.

Prioridad: alta.

Check 8 — ¿Existen tests automatizados de las reglas?

Qué comprobar. Sin tests con @firebase/rules-unit-testing, cada cambio en las reglas es una regresión potencial. Las reglas son código, deben tener pruebas.

Cómo hacerlo. Busca en tu repositorio:

Detectar test suite de reglas en el repo
1grep -RE "rules-unit-testing|initializeTestEnvironment" --include="*.js" --include="*.ts" . | head

Pinta vulnerable. Cero resultados en un repo con firestore.rules o database.rules.json versionados.

Prioridad: media. No introduce un agujero hoy, pero garantiza que aparecerá uno mañana.


Categoría 3: Cloud Functions

Check 9 — ¿Qué cuenta de servicio ejecuta tus Cloud Functions y qué roles tiene?

Qué comprobar. Por defecto, Cloud Functions corre con la cuenta [email protected] que en proyectos creados antes del cambio de defaults de Google Cloud en 2024 hereda el rol roles/editor. Los proyectos nuevos ya no la otorgan automáticamente, pero los heredados siguen vulnerables hasta que se revise. Eso significa que cualquier RCE o SSRF en una función equivale a control total del proyecto.

Cómo hacerlo.

Listar funciones y sus cuentas de servicio
1# Lista funciones con su service account
2gcloud functions list --format="table(name,serviceAccountEmail,region)"
3
4# Roles del App Engine default service account
5gcloud projects get-iam-policy TU_PROJECT_ID \
6--flatten="bindings[].members" \
7--filter="bindings.members:appspot.gserviceaccount.com" \
8--format="value(bindings.role)"

Pinta vulnerable. Cualquier rol del tipo roles/editor, roles/owner, roles/iam.serviceAccountUser global o roles/cloudfunctions.admin asignado a la cuenta por defecto.

Prioridad: crítica si hay roles/editor o roles/owner.

Cambiar la service account de funciones en producción puede romper accesos legítimos a Firestore, Storage o Secret Manager. Antes de retirar permisos, despliega cuentas dedicadas por función con los roles mínimos (roles/datastore.user, roles/storage.objectAdmin sobre buckets concretos, roles/secretmanager.secretAccessor sobre secretos concretos) y prueba en staging.

Check 10 — ¿Hay HTTP Functions accesibles sin verificación de token?

Qué comprobar. Las funciones desplegadas con functions.https.onRequest no validan tokens automáticamente. Si la lógica interna no lo hace, son endpoints públicos.

Cómo hacerlo. Lista las funciones y prueba sin auth:

Detectar HTTP Functions invocables sin token
1# URLs de funciones HTTP
2gcloud functions list --filter="httpsTrigger:*" \
3--format="value(httpsTrigger.url)"
4
5# Probar cada una sin Authorization
6for url in $(gcloud functions list --filter="httpsTrigger:*" --format="value(httpsTrigger.url)"); do
7echo "=== $url ==="
8curl -sS -o /dev/null -w "HTTP %{http_code}\n" "$url"
9done

Pinta vulnerable. Cualquier 200, 204 o respuesta con datos a una función que debería requerir auth. Un 401/403 indica que la función rechaza correctamente.

Prioridad: alta si la función modifica datos, envía emails o consume cuota; crítica si llega a procesar pagos.

Check 11 — ¿Las funciones leen secretos de Secret Manager o están hardcoded?

Qué comprobar. Claves de Stripe, SendGrid, OpenAI o cualquier API externa nunca deben estar en el código ni en variables de entorno desplegadas con firebase functions:config:set (deprecado y visible en firebase functions:config:get).

Cómo hacerlo.

Inspeccionar configuración legacy y variables de entorno
1# Configuración legacy (deprecada pero aún usada)
2firebase functions:config:get
3
4# Variables de entorno actuales
5gcloud functions describe NOMBRE_FUNCION \
6--region=europe-west1 \
7--format="value(serviceConfig.environmentVariables)"
8
9# Lista los secretos disponibles en Secret Manager
10gcloud secrets list

Pinta vulnerable. Cualquier valor que parezca una API key (sk_live_..., tokens largos en base64, claves privadas RSA) en functions:config:get o en el output de environmentVariables.

Prioridad: alta. Si encuentras una clave activa, rótala inmediatamente y migra a Secret Manager.


Categoría 4: Cloud Storage

Check 12 — ¿El bucket por defecto permite listado o lectura pública?

Qué comprobar. Los buckets de Firebase Storage son buckets de Google Cloud Storage por debajo. Un IAM mal configurado permite listado anónimo aunque las Security Rules estén bien.

Cómo hacerlo.

Auditar IAM y permisos del bucket
1# IAM del bucket — busca allUsers o allAuthenticatedUsers
2gsutil iam get gs://TU_PROJECT_ID.appspot.com
3
4# Permisos efectivos
5gcloud storage buckets describe gs://TU_PROJECT_ID.appspot.com \
6--format="value(iamConfiguration)"

Pinta vulnerable. Cualquier binding con allUsers o allAuthenticatedUsers, especialmente con roles roles/storage.objectViewer o roles/storage.objectAdmin. También sospecha si iamConfiguration.uniformBucketLevelAccess.enabled es false — significa que las ACLs por objeto pueden estar haciendo lo que quieran.

Prioridad: crítica si hay binding público con permisos de escritura, alta si solo lectura.

Check 13 — ¿Las Storage Rules limitan tamaño y contentType en escrituras?

Qué comprobar. Sin límites, un atacante puede usar tu bucket como hosting gratis (malware, contenido ilegal, distribución pirata) y dispararte la factura. Sin contentType validado, puede subir HTML/JS y ejecutarlo en tu dominio.

Cómo hacerlo.

Descargar y revisar storage.rules
1firebase deploy --only firestore:rules,storage --dry-run --project TU_PROJECT_ID 2>&1 | head -60
2
3# O directamente desde el repo
4grep -nE "request\.resource\.size|contentType" storage.rules || echo "FALTA: sin límites en storage.rules"

Pinta vulnerable. Cero menciones de request.resource.size o contentType.matches(...) en reglas con allow write.

Prioridad: alta.


Categoría 5: Hosting, App Check y secretos

Check 14 — ¿App Check está en modo enforcement para Firestore, Storage y Functions?

Qué comprobar. App Check verifica que las peticiones vienen de tu app real (reCAPTCHA Enterprise en web, Play Integrity en Android, DeviceCheck en iOS). Sin enforcement, las peticiones de bots y scripts pasan igual.

Cómo hacerlo. En la consola: Firebase Console → App Check → APIs. Cada producto (Firestore, Storage, Realtime Database, Cloud Functions) debe estar en "Enforced", no "Unenforced" ni "Monitored".

Por CLI:

Comprobar estado de App Check vía API
1# Requiere token de acceso
2TOKEN=$(gcloud auth print-access-token)
3curl -sS -H "Authorization: Bearer $TOKEN" \
4"https://firebaseappcheck.googleapis.com/v1/projects/TU_PROJECT_ID/services" | \
5python3 -m json.tool

Cada service debe tener enforcementMode: "ENFORCED".

Pinta vulnerable. enforcementMode: "OFF" o "UNENFORCED" en producción.

Prioridad: alta. Es la diferencia entre "tengo reglas" y "mis reglas se aplican a clientes legítimos únicamente".

Check 15 — ¿Hosting tiene CSP y cabeceras de seguridad configuradas?

Qué comprobar. Firebase Hosting no añade Content-Security-Policy, Strict-Transport-Security, X-Frame-Options ni X-Content-Type-Options por defecto. Sin ellas, una vulnerabilidad XSS en tu app es directamente explotable.

Cómo hacerlo.

Inspeccionar cabeceras desde curl
1curl -sSI https://tu-app.web.app | grep -iE "content-security-policy|strict-transport|x-frame|x-content-type"

Si no devuelve nada, no hay cabeceras. Configúralas en firebase.json:

firebase.json — cabeceras mínimas
1{
2"hosting": {
3 "public": "dist",
4 "headers": [
5 {
6 "source": "**/*",
7 "headers": [
8 { "key": "Strict-Transport-Security", "value": "max-age=63072000; includeSubDomains; preload" },
9 { "key": "X-Content-Type-Options", "value": "nosniff" },
10 { "key": "X-Frame-Options", "value": "DENY" },
11 { "key": "Referrer-Policy", "value": "strict-origin-when-cross-origin" },
12 { "key": "Content-Security-Policy", "value": "default-src 'self'; script-src 'self' https://www.gstatic.com https://www.google.com; connect-src 'self' https://*.googleapis.com https://*.firebaseio.com wss://*.firebaseio.com" }
13 ]
14 }
15 ]
16}
17}

Pinta vulnerable. Ausencia de CSP, HSTS o X-Content-Type-Options. La CSP es la que más cuesta afinar — empieza en modo Content-Security-Policy-Report-Only y refínala antes de aplicar enforcement.

Prioridad: media en general, alta si tu Hosting renderiza datos de usuario sin sanear.


Resumen: matriz de priorización

Si te queda media tarde para arreglar todo lo crítico, este es el orden:

  1. Crítico (hoy): checks 1, 5 — reglas con if true y Realtime Database respondiendo a /.json sin auth.
  2. Crítico/alto (esta semana): checks 2, 9, 12 — auth != null global, Cloud Functions con roles/editor, buckets públicos.
  3. Alto (próximas dos semanas): checks 3, 6, 7, 10, 11, 13, 14 — verificación de email, validación de schema, comodines recursivos, HTTP Functions sin token, secretos hardcoded, límites en Storage, App Check enforcement.
  4. Medio (siguiente sprint): checks 4, 8, 15 — custom claims, tests de reglas, cabeceras de Hosting.

La diferencia entre una auditoría y una buena intención es completar los puntos críticos en plazo. Si bloqueas un sprint para los cinco primeros, ya has eliminado la mayoría del riesgo real.


Errores que vemos en cada auditoría Firebase

Antes de pasar al cierre, tres patrones que aparecen en casi todas las auditorías que hacemos a SaaS y startups con Firebase en producción.

El check 9 sale mal en el 60% de los proyectos. No por descuido, sino porque la cuenta de servicio por defecto se asigna automáticamente y el rol Editor venía heredado de cuando el proyecto se creó hace dos años. Nadie lo tocó porque "funcionaba". Cambiarlo es la modificación de mayor impacto/esfuerzo en una auditoría Firebase.

Las reglas se testean cero veces. El check 8 falla en casi todos los repos que vemos. La barrera no es técnica — firebase emulators:exec tarda diez minutos en dejarse configurar — es prioritaria. Hasta que un equipo no se queme con una regresión, no invierte el tiempo. Si vas a invertir 20 minutos a la semana en seguridad Firebase, mete tests de reglas en CI antes que cualquier otra cosa.

App Check vive en "Monitored" eternamente. El check 14 normalmente está activado pero sin enforcement, porque alguien tuvo miedo de bloquear a clientes reales y se quedó en monitorización. Pasar a Enforced requiere mirar las métricas de App Check durante una semana y verificar que el rate de "validated" es alto. Si lo es, activa enforcement el viernes y ten al equipo localizado el sábado.


Cómo encaja Vulnerabbit en esta auditoría

Los 15 chequeos de este checklist son ejecutables manualmente en una tarde. La razón por la que la mayoría de equipos no los ejecuta es que necesitan repetirse cada vez que cambian las reglas, se despliega una función nueva o se rota una clave. Esa repetición es lo que automatiza un escáner.

La cobertura de Firebase de Vulnerabbit está construida exactamente sobre este patrón: parsea tus reglas de Firestore, Realtime Database y Storage; consulta los IAM de las cuentas de servicio de Cloud Functions vía API de Google Cloud; verifica el estado de App Check por producto; y comprueba las cabeceras de Hosting servidas. Cuando una regla cambia a una versión más permisiva, cuando aparece una HTTP Function nueva sin auth, o cuando un bucket cambia su IAM a público, llega una alerta con la diferencia exacta.

Para SaaS multi-tenant — donde el coste de una fuga entre clientes es desproporcionado al tamaño del bug — esta capa de monitorización continua es lo que separa un Firebase auditado una vez al año de un Firebase auditado todos los días. Si tu producto encaja en ese perfil, mira también cómo cubrimos seguridad para SaaS de extremo a extremo, donde Firebase es uno de los componentes pero conviven con APIs, infraestructura cloud y exposición externa que también necesitan vigilancia.

¿Necesitas auditar tu Firebase ya mismo?

La plataforma Firebase de Vulnerabbit ejecuta este checklist y muchos más controles de forma continua, te avisa cuando algo cambia y te da el comando exacto del fix. Empieza hoy con el escaneo gratuito de tu superficie externa para tener una primera vista de qué está expuesto.

Lanzar escaneo gratuito → · Ver /solutions/firebase →

Comprueba si tu dominio es vulnerable

Nuestro escáner gratuito analiza SSL, cabeceras de seguridad, DNS y configuración de email en menos de 5 minutos. Sin instalar nada.

Lanzar Auditoría Gratuita
Kevin Reyes

Escrito por

Kevin Reyes

Fundador & CEO de Vulnerabbit

Ingeniero DevSecOps con más de 7 años de experiencia en cloud security, CI/CD y automatización de infraestructura. Fundó Vulnerabbit con la misión de hacer la ciberseguridad sencilla, proactiva y accesible para startups y pymes.

LinkedIn
Escanea tu dominio gratis