Guía

¿Cómo reviso si mi app hecha con IA es segura?

Haz siete revisiones a tu app publicada y a su proyecto: claves secretas en el código que carga el navegador, tablas de Supabase sin seguridad por filas, endpoints que responden sin iniciar sesión, buckets de archivos públicos, falta de límites de uso, secretos en el repositorio y reglas que solo cuida la pantalla. Cada una toma minutos. Hazlas solo en tu propia app. Cada revisión puede mostrar un problema. Si una no encuentra nada, solo quiere decir que no encontró nada donde buscaste.

Una mano archiva una tarjeta en un organizador de escritorio donde las demás ya están en orden.

1. Claves secretas en el código que carga el navegador

Todo lo que tu app manda al navegador lo puede leer cualquiera. Algunas variables se copian al código del navegador a propósito: en una app con Vite, las que empiezan con VITE_; en una con Next.js, las que empiezan con NEXT_PUBLIC_. Una clave secreta guardada ahí ya es pública.

  • Cómo revisarlo: abre tu app publicada, abre las herramientas de desarrollador del navegador y busca en los scripts cargados sk_live_ y rk_live_ (las claves secreta y restringida de Stripe, las dos secretas), sb_secret_ (clave secreta de Supabase) y el nombre de cada servicio de IA, correo o cobro que use tu app. El navegador solo tiene los scripts de las páginas que abriste, así que inicia sesión y abre todas antes de buscar; si no encuentra nada, eso solo vale para lo que ya cargó.
  • Revisa también el código, que cubre las páginas que no abriste: busca en el proyecto todas las variables que empiezan con VITE_ o NEXT_PUBLIC_ y confirma que ninguna guarda un secreto, y después busca los mismos prefijos de clave en el código que corre en el navegador, por si alguien pegó una clave directamente.
  • Busca también eyJ, sin importar qué claves muestre tu proyecto. Las claves anteriores de Supabase son dos claves largas que empiezan con eyJ, y la palabra service_role no aparece en texto simple dentro de la secreta, así que buscarla no encuentra nada. Compara cada clave que encuentres con la anon key y la service_role de la página de claves de API en la configuración de tu proyecto de Supabase. Una clave que coincide con la service_role es la secreta, y está pública.
  • Lo que no es una fuga: una clave de Stripe que empieza con pk_ y una clave de Supabase que empieza con sb_publishable_. Las dos están hechas para ser públicas. Una clave que empieza con eyJ es pública solo si coincide con la anon key, como se explica arriba.
  • Cómo se arregla: pasa la llamada que necesita la clave a una función en el servidor, guarda la clave en la configuración del servidor y después rótala. La anterior ya quedó expuesta.

2. Tablas de Supabase sin seguridad por filas

Si tu app guarda sus datos en Supabase, el navegador lee la base de datos con la clave publicable. Esa clave es pública, así que lo único que detiene a un extraño es la seguridad por filas (RLS, por sus siglas en inglés). El propio Security Advisor de Supabase marca como error una tabla pública sin RLS: cualquiera con la URL de tu proyecto puede leer, editar y borrar todos sus datos.

  • Cómo revisarlo: en el panel de Supabase abre el Security Advisor y busca "RLS Disabled in Public". Luego lee cada política: una política de lectura para el rol anon o public (o sin rol indicado) cuya regla es solo true deja que cualquier visitante lea todas las filas.
  • Después pruébalo con dos peticiones a la API, una solo con la clave publicable y otra como un usuario de prueba con sesión. Una política para el rol authenticated sin revisar la fila deja que cualquier usuario con sesión lea las filas de todos, y las pantallas de tu app no lo muestran. La guía sobre Supabase, en las respuestas relacionadas más abajo, trae las dos peticiones.
  • Cómo se arregla: activa RLS en todas las tablas del esquema public y escribe políticas que liguen cada fila con su dueño, por ejemplo auth.uid() = user_id. Agrega las políticas en el mismo cambio y después prueba la app: con RLS activado y sin políticas, las pantallas que leen la tabla se quedan vacías. La guía sobre Supabase, en las respuestas relacionadas más abajo, lo explica paso a paso.

3. Endpoints que responden sin iniciar sesión

Las funciones de servidor y las rutas de API suelen tener más acceso que un visitante. Si una no revisa quién la llama, le responde a cualquiera que encuentre su dirección. MITRE lo registra como CWE-306: falta de autenticación en una función crítica.

  • Cómo revisarlo: abre tu app con la pestaña Network de las herramientas de desarrollador, usa una función y copia una de tus propias peticiones. Elige una que solo lea, o que cambie datos de prueba: si la función está abierta, la copia sí se ejecuta. Mándala otra vez desde una terminal como lo haría un extraño: sin la cookie de sesión y, si la petición lleva un token de usuario, con la clave pública de tu app en su lugar. Si aun así devuelve datos o cambia algo, está abierta. Un rechazo solo muestra que esa petición fue rechazada, así que repítelo con cada función y cada ruta de API.
  • Cómo se arregla: cada función revisa en el servidor quién inició sesión antes de leer o escribir, y rechaza la petición si no hay nadie.

4. Buckets de archivos públicos

Un bucket público en Supabase Storage se salta el control de acceso para leer: cualquiera que tenga la URL de un archivo lo puede abrir. Subir, borrar o mover sigue pasando por tus políticas. Público está bien para fotos de perfil o imágenes de un blog, no para facturas, identificaciones o contratos.

  • Cómo revisarlo: en el panel abre Storage y fíjate qué buckets son públicos. Abre en una ventana privada la URL de un archivo que debería ser privado. Un bucket privado está tan cerrado como sus políticas. Lístalas en el editor de SQL: select policyname, roles, cmd, qual, with_check from pg_policies where schemaname = 'storage' and tablename = 'objects'; Lee cada regla como en la revisión 2: una que no nombra un bucket_id aplica a todos los buckets, y una que nombra un bucket pero no revisa nada del usuario le abre ese bucket a todos los de su rol.
  • Cómo se arregla: haz privados los buckets con archivos privados y entrega cada archivo con una URL firmada que crea tu servidor y que vence después de un tiempo.

5. Sin límites de uso

Sin un límite, una persona o un script puede llamar a tu app todas las veces que quiera. Si cada llamada te cuesta, como un modelo de IA, un correo o un SMS, eso termina en una factura. OWASP lo incluye entre sus diez riesgos de seguridad de APIs como consumo de recursos sin restricción.

  • Cómo revisarlo: haz una lista de las funciones que llaman a un servicio de pago o mandan mensajes y busca un límite por usuario o por dirección. Si no lo encuentras en tu código ni en la configuración de tu hosting, da por hecho que no existe. Si lo encuentras, confirma que corre en el servidor y pruébalo: desde una terminal, llama la función unas veces más de las que permite el límite, diez llamadas como máximo, con un destinatario de prueba para que no salga ningún mensaje real. Si las llamadas que pasan del límite siguen funcionando, el límite no se aplica.
  • Cómo se arregla: un límite de cuántas veces cada usuario o dirección puede llamar cada función, más un tope de gasto en el panel de cada servicio de pago.

6. Secretos en el repositorio

Una clave que se subió una vez se queda en el historial de Git aunque borres el archivo. El secret scanning de GitHub revisa el historial completo de todas las ramas en busca de credenciales escritas en el código, gratis en repositorios públicos.

  • Cómo revisarlo: busca en el repositorio y en su historial archivos .env y los prefijos de clave de la revisión 1, incluido eyJ. En GitHub, abre las alertas de secret scanning del repositorio si las tiene.
  • Cómo se arregla: rota de inmediato cada clave que encuentres. El propio GitHub aconseja eso, y dice que limpiar el historial es lento y muchas veces innecesario una vez que la clave está revocada.

7. Reglas que solo cuida la pantalla

Esconder un botón no es un permiso. Si solo la página decide quién entra al panel de administración o quién puede borrar, cualquiera puede saltarse la página y llamar directo a la base de datos o a la función. MITRE lo registra como CWE-602: seguridad del servidor que depende del cliente.

  • Cómo revisarlo: para cada cosa que solo algunos usuarios pueden hacer, como entrar al panel de administración, ver registros de otros o hacer reembolsos, copia la petición con la sesión de alguien que sí puede, como en la revisión 3. Mándala otra vez con la sesión de una cuenta normal en lugar de la primera. Prueba también con el id de un registro de otro usuario y con un cambio a un campo que no deberías controlar, como tu propio rol. Si algo de eso funciona, la regla solo vive en la pantalla. Usa solo cuentas y registros de prueba: si la regla falta, el reembolso, el borrado o el cambio de rol sí ocurre.
  • Cómo se arregla: pon la regla donde están los datos, en una política de RLS o en una revisión en el servidor. El botón escondido queda solo para ordenar la pantalla.

Dónde entra LOJIK

Todo lo anterior lo puedes hacer por tu cuenta. Si quieres una segunda mirada, la página de Rescate de LOJIK es para apps hechas con herramientas como estas que se quedaron a medias.

Ver la página de Rescate

La página abre con un autodiagnóstico gratis. Sin iniciar sesión.

Cuándo LOJIK no es la opción adecuada

Probablemente LOJIK no sea la mejor opción si:

  • Ya tienes un desarrollador o un equipo de seguridad que hace estas revisiones.
  • Un cliente o un regulador te pide una auditoría formal de seguridad o una prueba de penetración. Contrata a una firma que haga ese trabajo.
  • La app no es tuya. Revisa solo tus propias apps, o las de alguien que te dio permiso por escrito.

Preguntas

¿La anon key de Supabase es secreta?

No. La clave publicable, que en proyectos anteriores se llama anon key, está hecha para ser pública: solo alcanza lo que permite la seguridad por filas. La clave secreta, service_role en proyectos anteriores, se salta la seguridad por filas y nunca debe estar en el navegador.

¿Qué reviso primero?

Las dos que un extraño alcanza sin cuenta: claves secretas en el código del navegador y tablas sin seguridad por filas. Si una clave secreta quedó expuesta, rótala de inmediato y pasa a tu servidor la llamada que la usa.

¿Puedo hacer estas revisiones en la app de otra persona?

No. Hazlas solo en tu propia app, o con permiso por escrito de quien es dueño de ella.

Los nombres de producto pertenecen a sus dueños. LOJIK no tiene relación con ellos y los menciona solo para decir de qué herramienta se trata.

Fuentes