Victorious English Academy — página y plataforma
Solo miramos lo que cualquier visitante recibe al abrir la página: la portada, los archivos de diseño y de funciones, y la configuración que viaja con ellos. No se enviaron formularios, no se probaron claves ni usuarios, no se crearon cuentas y no se atacó el sitio de ninguna manera. Fue una mirada, no una prueba de intrusión.
Son instrucciones que el sitio le da al navegador para protegerse de trucos conocidos (por ejemplo, que otra página lo meta dentro de la suya para engañar al visitante). Hoy no están puestas.
Cómo se arregla: agregar un archivo de configuración en Netlify con esas reglas. Es un cambio de una sola vez, sin tocar el diseño. Nosotros lo podemos dejar listo.
El sitio ya exige conexión segura, pero esa exigencia no se extiende a posibles direcciones «hijas» (como app.tusitio.com). Mientras no uses subdominios, casi no importa.
Cómo se arregla: añadir una palabra a esa misma regla de conexión segura.
Cuando alguien usa el formulario, su nombre se arma dentro del enlace que abre WhatsApp. No es un fallo de seguridad, pero es un dato personal que queda en la dirección y pasa por un tercero.
Cómo se arregla: pedir el nombre solo cuando de verdad se necesite, o no incluirlo en el enlace.
No es un fallo. Es una buena práctica: un archivo estándar con un correo de contacto, para que quien detecte algo sepa a quién avisar.
Cómo se arregla: publicar un pequeño archivo de contacto. Cinco minutos.
La app guarda cuentas y progreso en Supabase, y habla con esa base de datos directamente desde el navegador de cada alumno. Eso es normal y moderno, pero significa que toda la protección de los datos vive en la configuración de Supabase, no en el código de la página.
La llave que viaja en la web es pública a propósito (así se diseñó), así que no es el problema. El problema aparece si las tablas de alumnos no tienen activada la «seguridad por fila»: en ese caso, cualquiera con esa llave podría leer o modificar el progreso y los datos de otros alumnos. No se pudo comprobar desde afuera sin meternos en datos de la gente, así que hay que verificarlo por dentro.
Cómo se verifica: en el panel de Supabase, confirmar que las tablas
profiles, student_progress, quiz_attempts y
onboarding_responses tengan RLS activo, con políticas que aten cada fila a su dueño
(el usuario solo ve y edita lo suyo). El propio panel tiene un «Security Advisor» que lo marca.
Las lecciones, el vocabulario y los retos están dentro del código de la página. El inicio de sesión es, por ahora, una cortina visual: quien abra la página puede ver el contenido sin cuenta. Importa solo si ese contenido es exclusivo de alumnos de pago.
Cómo se arregla: servir el contenido de alumnos desde la base protegida, no desde la página.
La sesión del alumno se guarda en el navegador. Sin las «reglas de blindaje» (sobre todo la lista de orígenes de confianza, CSP), si se colara un script ajeno, podría robar esa sesión. Es el mismo arreglo que recomendamos para la página, y aquí pesa más porque hay cuentas.
Cómo se arregla: poner la CSP y sacar el código a archivos aparte para poder cerrarla bien.
La contraseña mínima es de 6 caracteres (mejor 8, con confirmación por correo y bloqueo de contraseñas filtradas, todo desde Supabase). La librería de Supabase se carga «a la última versión» sin fijar cuál, lo ideal es fijarla. Y faltan las mismas cabeceras de blindaje que la página.
Hoy el sitio vive en Netlify, que funciona bien para una página. Pero como esto va a ser una plataforma con usuarios y cuentas, vale la pena considerar mudarla a Cloudflare (Pages y Workers). Trae de fábrica protección contra ataques de saturación y bots, un cortafuegos de aplicaciones, y una red más robusta y con mejor costo a medida que crece. Es el mismo tipo de base sobre la que Modular Mango construye sus plataformas, así que te podemos acompañar en esa mudanza si tu amigo quiere.
Si estás construyendo el sitio con Claude Code, puedes pegarle estas preguntas y pedirle que las resuelva. Van de lo más importante a lo opcional.
Si usas Supabase (o cualquier base de datos conectada desde el navegador), lo más urgente es confirmar que cada tabla tenga activada la «seguridad por fila» (RLS) con sus políticas. Sin eso, la llave pública que viaja en la web deja que cualquiera lea o borre toda la base.
Pídele a tu Claude: «Revisa si mis tablas de Supabase
profiles, student_progress, quiz_attempts y
onboarding_responses tienen RLS activado con políticas que aten cada fila a
auth.uid(); muéstrame cuáles están abiertas y ayúdame a cerrarlas sin romper la app».
Pídele a tu Claude: «Crea un archivo _headers en el proyecto de Netlify con
Content-Security-Policy (primero en modo solo-reporte), X-Content-Type-Options, X-Frame-Options,
Referrer-Policy y Permissions-Policy, adaptado a mi sitio».
Pídele a tu Claude: «Revisa que ninguna llave privada o secreto esté en el código del frontend; las que deban ser públicas, confírmame que lo sean; mueve el resto a variables de entorno».
Pídele a tu Claude: «Crea un /.well-known/security.txt con un correo de contacto».