Fix: Bolt × seguridad

Bolt puso tu app en una pestaña del navegador.
Asegurate de que tus keys no estén ahí adentro.

Una auditoría de securityscanner.dev encontró que el 15% de las apps de Bolt exponía API keys hardcodeadas en el código del frontend. Y las keys expuestas no se quedan quietas: los bots scrapean los bundles recién deployados en cuestión de horas, y una key de OpenAI filtrada se convierte en la factura de uso de otro. Nada de esto significa que tu idea de producto estuviera mal. Significa que shippeó el patrón de demo — y los demos guardan secrets en lugares donde producción no puede.

Por qué las apps de Bolt filtran: el camino más corto a un demo que anda

Bolt construye y corre tu app adentro de WebContainers — todo se ejecuta en el navegador, y justo por eso se siente instantáneo. Pero esa arquitectura empuja el código generado hacia el todo-client-side: cuando le pedís a Bolt que integre OpenAI o Stripe, la versión que anda más rápido pone la key directo en el código del frontend, y eso es lo que suele tocarte. vibeappscanner.com documenta las vulnerabilidades recurrentes en casi cada app de Bolt que audita: keys service_role de Supabase expuestas, políticas RLS faltantes, y auth atornillado que renderiza una pantalla de login sin que la API verifique jamás quién llama. El input de usuario sin escapar suma stored XSS a la lista. Cada una es invisible en el preview. Todas las encuentra trivialmente cualquiera que abra devtools en tu app deployada.

El checklist de seguridad de Bolt — corrélo antes de que lo corra otro

Buscá keys en tu bundle

Deployá, mirá el código fuente y buscá 'sk-', 'sk_live' y 'service_role'. Bolt hardcodea seguido keys de terceros en archivos del frontend porque así andaba el demo. Cada hit necesita rotación, no solo borrado.

La key service_role en código del cliente

La key service_role de Supabase saltea todas las políticas RLS. Si aparece en cualquier lugar al que llegue el navegador, tu base de datos no tiene ningún control de acceso — sin importar qué políticas escribiste.

Auth que solo existe en la UI

Una página de login no prueba nada. Llamá a tus endpoints de la API directo con curl, sin autenticar. Si vuelven datos, tu auth es decoración — Bolt suele gatear la interfaz sin gatear la API.

Input de usuario sin escapar

Los componentes que genera Bolt suelen renderizar contenido de usuarios sin sanitizar. Un stored XSS en un campo de comentarios y un atacante corre JavaScript en la sesión de todos tus demás usuarios.

Nada que frene a un atacante

Probá 30 logins seguidos con contraseña incorrecta contra tu propia app. Si nada te frena, el credential stuffing corre a velocidad máxima contra cada cuenta que tenés.

La pared: un server que se niega a arrancar sin sus secrets

En useDeploy los secrets no están desparramados por los archivos fuente — viven en variables de entorno, y un schema de Zod los valida todos al boot. Si falta uno, el proceso sale con un error con nombre en vez de renguear hasta producción. Y como los secrets solo existen server-side, el bundle del cliente físicamente no puede filtrar lo que nunca recibe.

terminal — boot con un secret faltante
 1  $ bun run start
 2  ✗ Invalid environment:
 3      BETTER_AUTH_SECRET   Required
 4      DATABASE_URL         Required
 5  error: process exited with code 1
 6  
 7  # los secrets se validan al boot, solo server-side.
 8  # el bundle no puede filtrar lo que el cliente nunca recibe.

Lo que ya viene resuelto en la base

Secrets validados al boot

Un único schema de env validado por Zod es la única casa de cada credencial. Nada secreto se puede importar desde código del cliente, así que 'la key en el bundle' deja de ser una clase de bug posible.

Webhooks de billing que sobreviven replays

Los webhooks de pago son idempotentes por externalOrderId sobre un outbox durable — un provider reintentando el mismo evento dos veces no puede cobrarle ni acreditarle doble a nadie.

Webhooks salientes firmados

Los webhooks que manda tu app van firmados con HMAC, así los receptores pueden verificar que vinieron de vos — la disciplina que querés de los dos lados del caño de webhooks.

Rate limiting por tier

El tráfico de lectura, escritura y auth tiene cada uno su propio limiter, con auth bucketeado por IP y por email. El test de las 30 contraseñas incorrectas del checklist de arriba acá se topa con un 429.

Aislamiento que no es opcional

Repositorios org-scoped y un query guard de Prisma hacen que las lecturas cross-tenant devuelvan 404. No existe el equivalente de 'me olvidé de prender RLS' porque no hay switch para dejar apagado.

Una suite de tests mirando todo

450+ tests automatizados más un gate E2E de Playwright en CI ejercitan los flujos de auth, la idempotencia de webhooks y las fronteras de tenant en cada merge.

Ruta de migración: del WebContainer a un backend de verdad

Exportá tu proyecto desde Bolt — descargá el código o pusheálo a GitHub. La UI React viaja bien; lo que no viaja es la arquitectura de todo-en-el-navegador. La migración es sobre todo una mudanza: cada fetch que llama a una API de terceros con la key embebida se vuelve una llamada a tu propio endpoint tipado, y la key se muda al env del server donde el schema de Zod la valida al boot. La lógica de negocio que vivía en componentes se vuelve use cases detrás de controllers de Express, tipados de punta a punta vía el contrato OpenAPI generado. Rotá cada key que alguna vez apareció en código generado por Bolt — asumí que todas se filtraron — y hacé la mudanza de a una integración: OpenAI primero si ahí está tu riesgo de gasto, Stripe después, y el resto. Tus usuarios ven la misma app. Tus keys dejan de ser públicas.

Bolt × seguridad, sin vueltas

Mi key de OpenAI ya se filtró. ¿Qué hago primero?+
Rotála ahora, antes de cualquier refactor — revocá la key vieja en el dashboard de OpenAI y poné límites de gasto. Después revisá el billing por uso que no reconozcas. Recién cuando la key esté muerta vale la pena mover la llamada al server para que la nueva no se filtre igual.
¿Puedo quedarme con Supabase mientras migro?+
Sí — corré los dos durante la transición. Mantené RLS prendido en todo lo que siga sirviendo Supabase, y mudá las tablas al schema de Prisma de la base feature por feature. El tenant guard toma el control a medida que cada tabla cruza.
¿La base maneja bien los webhooks de Stripe?+
Sí, y es un lugar donde los handlers generados por IA fallan seguido: los providers reenvían eventos, y los handlers no idempotentes los procesan doble. La base dedupea por externalOrderId sobre un outbox durable, así que los replays se absorben.
¿Esto es un rewrite?+
El frontend no — los componentes y los estilos se portan. El backend en la práctica sí, y ese es el punto: el backend que vive en el navegador es la parte que filtra keys. Pero en la base no reconstruís auth, billing ni tenancy; enchufás tu lógica de producto en unos que ya existen.

Una licencia. Dos formas de tenerla.

CORE

Founder

$99pago único

Un snapshot congelado de UseDeploy. Tuyo para siempre, sin updates.

  • Descarga del zip con versión frozen
  • Código completo, uso comercial, proyectos ilimitados
  • Los 800+ tests · todas las páginas de docs
  • Refund a 14 días
RECOMMENDED

LATEST + UPDATES

Lifetime

$199$249pago único

Siempre el último UseDeploy. Re-descargá cada release, gratis.

  • Zip de la última versión — re-descargable para siempre
  • Cada release futuro sin cargo extra
  • Soporte Discord priority
  • Los 800+ tests · todas las páginas de docs
  • Refund a 14 días

Shippeá la app sin shippear tus keys

Quedate con la velocidad que te trajo hasta acá. Mudate a una base donde los secrets se validan al boot, los webhooks son idempotentes y 450+ tests cuidan el piso.