
Revisamos desde dentro la seguridad de este portfolio: chat, API de mensajes, servidor y 15 demos conceptuales. Hallamos 8 fallos, entre ellos inyección de script en el origen del chat, y los corregimos todos.
Contexto
Este sitio es más que páginas estáticas. Tiene un chat sobre VITON ID (Firebase Auth y Firestore), una pequeña API en Node que guarda los mensajes del formulario y envía correos, un servidor nginx en Docker y copias de 15 sitios conceptuales en /demos/. El 28 de septiembre de 2026 lo revisamos todo desde dentro: el código, las reglas de Firestore, la configuración del servidor y de los contenedores y las dependencias npm.
Para cada parte hicimos la pregunta de un atacante: qué puede hacer con ella alguien de fuera y cuánto le costaría al propietario. Un hallazgo solo contaba si lo reproducíamos, y cada corrección se volvió a probar en una copia local de la configuración de producción antes de publicarla.
8
hallazgos de seguridad, todos corregidos
Fuente: Revisión de código, 28 sept. 2026
1
fallo de riesgo alto: inyección de script en el origen del chat, cerrado
Fuente: Reproducido antes y después de la corrección en una copia local
119
páginas de demos cargadas con la nueva política de seguridad, 0 infracciones
Fuente: Comprobación con Chrome headless, 28 sept. 2026
0
vulnerabilidades conocidas en las dependencias npm del sitio y la API
Fuente: npm audit, 28 sept. 2026
Enfoque
Leímos cada ruta de la API, la verificación de tokens, las reglas de Firestore con sus 39 pruebas en el emulador, los archivos de nginx y Docker Compose y el código de las 15 demos. En lugar de atacar el servidor real, levantamos los mismos contenedores en local (nginx y la API con la configuración de producción) y probamos allí cada ataque.
El hallazgo más grave estaba en una demo, no en el propio sitio. El catálogo de Oriva insertaba el parámetro ?cat= de la dirección en la página como HTML, así que un enlace preparado ejecutaba script en tarasovvitalii.com, el origen donde el chat guarda su sesión de Firebase. Un clic del propietario habría bastado para entregar la bandeja de entrada. Las otras 14 demos solo comparan los parámetros con valores conocidos.
Correcciones: la demo solo acepta categorías y órdenes conocidos; /demos/ tiene su propia Content-Security-Policy; Leaflet está fijado con hashes de integridad; la redirección /demos/<id> se construye con un patrón estricto; se rechazan las direcciones que podían colar campos en enlaces mailto:; un límite diario global protege el almacenamiento; la API funciona con un usuario sin privilegios y un sistema de archivos de solo lectura; la versión de nginx está oculta y HSTS cubre www.
Galería
Resultados
8 hallazgos, 8 corregidos: 1 alto, 1 medio y 6 bajos. El código que se ejecutaba en la demo antigua no hace nada en la nueva, y la petición que antes inyectaba una cabecera Set-Cookie ahora recibe un 404 normal. Las 119 páginas de demos cargan con la nueva política sin ninguna infracción, las pruebas de la API pasan 48 de 48 con las nuevas reglas cubiertas y npm audit no encuentra vulnerabilidades conocidas en las dependencias del sitio y de la API.
Revisado y correcto: la verificación de tokens de Firebase (solo RS256, id de clave obligatorio, audiencia, emisor y caducidad comprobados), las reglas de Firestore (nadie lee conversaciones ajenas ni escribe como el propietario), las plantillas de correo (todos los valores escapados, sin inyección de cabeceras), los registros (sin tokens, correos ni IP) y la interfaz del chat (el texto del visitante nunca se inserta como HTML).
Auditoría de seguridad
Cada hallazgo se reprodujo antes de contarlo y se volvió a probar tras la corrección, en una copia local de los contenedores de producción. Ordenados por riesgo.
-
Alto
Inyección de script en una demo, en el origen del chat
CorregidoAntes
El catálogo de Oriva insertaba el parámetro ?cat= en la página como HTML. Un enlace preparado ejecutaba script en tarasovvitalii.com, donde el chat guarda su sesión de Firebase: un clic del propietario podía exponer la bandeja de entrada y su cuenta.
Después
Solo se aceptan categorías y órdenes conocidos. El mismo código ya no se ejecuta, y las otras 14 demos se revisaron en busca del mismo patrón.
-
Medio
Demos sin política de seguridad y script externo sin control de integridad
CorregidoAntes
52 páginas de demos cargaban Leaflet desde unpkg.com sin hash de integridad, y /demos/ no enviaba Content-Security-Policy: un CDN comprometido o cualquier script inyectado llegaba a todo el origen.
Después
Leaflet está fijado con hashes SRI y /demos/ tiene su propia política: peticiones solo al propio sitio, scripts solo del sitio y del archivo fijado, sin plugins. 119 páginas, 0 infracciones.
-
Bajo
Inyección de cabeceras en una redirección del servidor
CorregidoAntes
La redirección de /demos/<id> a /demos/<id>/ devolvía la ruta decodificada tal cual, así que %0d%0a en un enlace escribía una cabecera extra en la respuesta, por ejemplo Set-Cookie.
Después
La redirección solo acepta letras, dígitos, guiones y guiones bajos y construye la dirección por sí misma. La misma petición recibe ahora un 404 normal.
-
Bajo
Campos ocultos en los enlaces «Responder» del propietario
CorregidoAntes
Una dirección como me@x.com?bcc=…&body=… pasaba la validación, y el enlace Responder de la bandeja y de los correos rellenaba una copia oculta y un texto. Los nombres también podían llevar caracteres de cambio de dirección que disfrazan el texto.
Después
Se rechazan las direcciones con delimitadores de URL, se codifican en los enlaces mailto: y se eliminan los caracteres de cambio de dirección. Nuevas pruebas de la API cubren los tres casos.
-
Bajo
Sin límite global para los mensajes guardados
CorregidoAntes
Los límites eran solo por dirección IP. Cambiando de dirección se podían seguir añadiendo envíos del formulario y de bots hasta llenar el disco.
Después
Un límite diario global de envíos guardados (500 por defecto) se suma a los límites por IP. Superado, el formulario responde «inténtalo más tarde».
-
Bajo
La API de mensajes se ejecutaba como root
CorregidoAntes
El contenedor de la API funcionaba como root, con un sistema de archivos escribible y todas las capacidades de Linux por defecto: cualquier fallo daría a un atacante un punto de apoyo cómodo.
Después
Funciona con el usuario sin privilegios node, sobre un sistema de archivos de solo lectura, sin capacidades de Linux y sin escalada de privilegios.
-
Bajo
Versión del servidor visible y rama de nginx obsoleta
CorregidoAntes
Cada respuesta llevaba «server: nginx/1.27.5», y el contenedor del sitio usaba la rama 1.27, que ya no recibe correcciones.
Después
La versión está oculta en todas las respuestas y el contenedor usa la rama estable actual de nginx.
-
Bajo
HTTPS obligatorio sin cubrir www
CorregidoAntes
La cabecera HSTS, que indica al navegador usar solo HTTPS, se enviaba en el dominio principal pero no en www, y no cubría subdominios.
Después
www también envía HSTS y la política incluye los subdominios, así que http://www… también pasa a HTTPS.
Lo que esto no demuestra
Es nuestro propio sitio, no un trabajo para un cliente. La revisión la hicimos nosotros a partir del código fuente; no es una prueba de penetración independiente ni un certificado. Reprodujimos los ataques en una copia local de los contenedores de producción, no contra el servidor real.
El resultado cubre lo revisado el 28 de septiembre de 2026: el código, las dependencias o las demos nuevas necesitan la misma revisión. Las demos siguen compartiendo el origen del sitio. La nueva política limita lo que podría alcanzar un código inyectado; llevar las demos a su propio dominio las aislaría por completo.
¿Planeas algo parecido? Nuestro fundador, Tarasov Vitalii, lee cada mensaje personalmente.
Encarga un proyecto similar en VITON13 Studio
Siguiente proyecto
Möbius School & InstituteWeb de colegio y universidad con área personal
Concepto2026
Todos los trabajos (32)
