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.
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:
- Qué comprobar — el chequeo concreto, en lenguaje claro.
- Cómo hacerlo — comando exacto (
firebase,gcloud,gsutil,curl) o ruta en la consola. - Pinta vulnerable — cómo se ve un resultado que indica problema.
- 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:
1# Login y selección de proyecto2firebase login3gcloud auth login4gcloud config set project TU_PROJECT_ID5firebase use TU_PROJECT_ID67# Descarga el estado actual de las reglas a local8firebase firestore:rules > /tmp/firestore.rules.actual9curl -s "https://TU_PROJECT_ID-default-rtdb.europe-west1.firebasedatabase.app/.json?shallow=true&access_token=$(gcloud auth print-access-token)" > /tmp/rtdb-shallow.json10gsutil ls -L -b gs://TU_PROJECT_ID.appspot.com 2>/dev/nullCon 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:
1grep -E "request\.time|timestamp\.date|if true" /tmp/firestore.rules.actualPinta 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.
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.
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.
1grep -nE "get\(/databases/" /tmp/firestore.rules.actualY para verificar qué claims tiene un usuario concreto:
1// node script2const admin = require('firebase-admin');3admin.initializeApp({ credential: admin.credential.applicationDefault() });45admin.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.
1# Reemplaza la URL por la de tu instancia2curl -sS "https://TU_PROJECT_ID-default-rtdb.europe-west1.firebasedatabase.app/.json?shallow=true" | head -c 500Pinta 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.
1grep -nE "hasOnly|hasAll|request\.resource\.data" /tmp/firestore.rules.actual | wc -lPinta 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.
1grep -nE "\{document=\*\*\}|\{[a-zA-Z]+=\*\*\}" /tmp/firestore.rules.actualPinta 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:
1grep -RE "rules-unit-testing|initializeTestEnvironment" --include="*.js" --include="*.ts" . | headPinta 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.
1# Lista funciones con su service account2gcloud functions list --format="table(name,serviceAccountEmail,region)"34# Roles del App Engine default service account5gcloud 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:
1# URLs de funciones HTTP2gcloud functions list --filter="httpsTrigger:*" \3--format="value(httpsTrigger.url)"45# Probar cada una sin Authorization6for url in $(gcloud functions list --filter="httpsTrigger:*" --format="value(httpsTrigger.url)"); do7echo "=== $url ==="8curl -sS -o /dev/null -w "HTTP %{http_code}\n" "$url"9donePinta 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.
1# Configuración legacy (deprecada pero aún usada)2firebase functions:config:get34# Variables de entorno actuales5gcloud functions describe NOMBRE_FUNCION \6--region=europe-west1 \7--format="value(serviceConfig.environmentVariables)"89# Lista los secretos disponibles en Secret Manager10gcloud secrets listPinta 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.
1# IAM del bucket — busca allUsers o allAuthenticatedUsers2gsutil iam get gs://TU_PROJECT_ID.appspot.com34# Permisos efectivos5gcloud 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.
1firebase deploy --only firestore:rules,storage --dry-run --project TU_PROJECT_ID 2>&1 | head -6023# O directamente desde el repo4grep -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:
1# Requiere token de acceso2TOKEN=$(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.toolCada 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.
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:
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:
- Crítico (hoy): checks 1, 5 — reglas con
if truey Realtime Database respondiendo a/.jsonsin auth. - Crítico/alto (esta semana): checks 2, 9, 12 —
auth != nullglobal, Cloud Functions conroles/editor, buckets públicos. - 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.
- 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.
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
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.
LinkedInContinúa leyendo
Seguridad en React y Next.js: las vulnerabilidades más frecuentes en 2026
Las vulnerabilidades más explotadas en apps Next.js 15/16: errores de frontera RSC, Server Actions, fugas de secretos, SSRF, cache poisoning y mitigaciones.
Alternativas a Rapid7 InsightVM para Pymes y Startups (2026)
Rapid7 InsightVM es el estándar enterprise en gestión de vulnerabilidades, pero rara vez encaja en pymes y startups. 5 alternativas reales según escala y presupuesto.
Alternativas a Detectify: Comparativa Honesta para Pymes (2026)
Detectify es un DAST sólido, pero no encaja en todas las pymes. Repasamos sus fortalezas, sus límites y 5 alternativas reales con pros, contras y para quién es cada una.