Respuesta breve
Una auditoría de seguridad web de caja blanca lee el código fuente y la configuración en lugar de adivinar desde fuera. En tarasovvitalii.com encontró ocho problemas el 28 de septiembre de 2026: uno alto (inyección de script a través de una demo en el origen del chat), uno medio (las demos no tenían política de seguridad) y seis bajos. Los ocho se corrigieron y se volvieron a probar en una copia de los contenedores de producción.
Un portafolio que merecía una revisión de seguridad
Un portafolio parece el tipo de web más seguro: unas cuantas páginas de trabajos y un formulario de contacto. tarasovvitalii.com, el sitio del fundador de VITON13, es más que eso. Tiene un chat sobre VITON ID, construido con Firebase Authentication y Firestore, una pequeña API en Node que guarda los mensajes de contacto y envía correos, un servidor nginx en Docker y copias de quince sitios conceptuales en /demos/. Un visitante que inicia sesión para escribir un mensaje confía a la vez en todas esas piezas.
El 28 de septiembre de 2026, VITON13 Studio revisó el sitio como revisaría el de un cliente antes del lanzamiento. Para cada parte, la pregunta era la de un atacante: ¿qué puede hacer con ella alguien de fuera y cuánto le costaría al propietario? Un hallazgo solo contaba una vez reproducido, y una corrección solo contaba cuando el mismo ataque había fallado contra ella.
Método: primero el código fuente, después ataques en una copia local
Una auditoría de seguridad web de caja blanca empieza desde dentro. Los revisores leyeron cada ruta de la API y el código que verifica los tokens de inicio de sesión, las reglas de Firestore con sus 39 pruebas en el emulador, la configuración de nginx y de Docker Compose, las dependencias npm del sitio y de la API, la interfaz del chat y el código de las quince demos.
En lugar de sondear el servidor real, el estudio levantó en local los mismos contenedores, nginx y la API con la configuración de producción, y probó allí cada ataque. Así, el sitio real y sus visitantes quedan fuera del experimento, y la misma petición se puede repetir antes y después de una corrección para demostrar el cambio. Solo el resultado final, es decir, las cabeceras y las redirecciones, se volvió a comprobar en la dirección pública.
Después, cada hallazgo confirmado recibió un nivel de gravedad. Alto significaba que alguien de fuera podía actuar dentro de la sesión de otra persona o llegar a datos privados; medio, que faltaba una capa de defensa capaz de convertir un fallo pequeño en uno grande; bajo, una debilidad que necesita condiciones poco habituales o que filtra poca información. La lista siguiente respeta ese orden, y cada punto recoge qué hacía el ataque antes de la corrección y qué hace la misma petición después.
El hallazgo de riesgo alto: inyección de script a través de una demo
El problema más grave no estaba en el propio portafolio, sino en una de sus demos. El concepto de tienda Oriva leía el parámetro ?cat= de la dirección y lo insertaba en la página como HTML. Por tanto, un enlace preparado podía ejecutar script en tarasovvitalii.com. OWASP describe esta clase de fallo, el cross-site scripting, como la inyección de script en un sitio en el que la víctima confía, y eso es exactamente lo que lo volvía peligroso aquí.
Las demos comparten origen con el chat, y el chat guarda su sesión de Firebase en el navegador, en ese mismo origen. Un solo clic del propietario del sitio en un enlace así podría haber expuesto la bandeja de entrada y la cuenta del propietario. Una vez encontrado, el arreglo es sencillo: la demo ahora solo acepta valores de categoría y de orden conocidos e ignora todo lo demás. Las otras catorce demos se revisaron en busca del mismo patrón, y solo comparan los parámetros de la dirección con valores fijos.
Una política de seguridad y scripts fijados para las demos
El hallazgo de riesgo medio tenía que ver con la defensa en profundidad. La sección /demos/ no enviaba Content-Security-Policy, la cabecera que indica al navegador de qué orígenes puede cargar una página scripts, estilos y datos. Además, 52 páginas de demos cargaban Leaflet, una biblioteca de mapas, desde un CDN público sin hash de integridad. Si ese CDN llegara a verse comprometido, o si se inyectara cualquier script, nada limitaría lo que podría alcanzar.
Ahora /demos/ tiene su propia política: peticiones solo al propio sitio, scripts solo del sitio y de la biblioteca fijada, sin plugins. Leaflet queda fijada con hashes de Subresource Integrity, así que el navegador rechaza el archivo si cambia un solo byte. En Chrome headless, las 119 páginas de demos cargaron bajo la nueva política con cero infracciones.
Inyección de cabeceras en una redirección del servidor
La regla de nginx que redirigía /demos/<id> a /demos/<id>/ devolvía la ruta decodificada en la cabecera Location. Por eso, un enlace que contuviera %0d%0a, el salto de línea codificado, podía añadir una cabecera propia a la respuesta, por ejemplo Set-Cookie. OWASP clasifica esto como inyección CRLF: un retorno de carro y un salto de línea colados en un punto donde el servidor construye cabeceras.
La redirección ahora solo acepta letras, dígitos, guiones y guiones bajos, y construye ella misma la dirección de destino. La misma petición que antes devolvía un 301 con una cookie inyectada recibe ahora un simple 404, tanto en la copia local como en el sitio real.
Cinco arreglos menores en correo, límites y servidor
Tres hallazgos bajos afectaban al sistema de mensajes. Una dirección de correo como me@x.com?bcc=…&body=… pasaba la validación, así que el enlace Responder de la bandeja del propietario podía abrirse con un destinatario oculto en copia y un texto ya escrito; ahora esas direcciones se rechazan y cada dirección de un enlace mailto: se codifica. Los nombres podían llevar caracteres de cambio de dirección que disfrazan el texto; ahora se eliminan. Los límites eran solo por dirección IP, así que rotando direcciones se podía llenar el disco; ahora hay además un tope global diario de 500 envíos guardados.
El resto eran ajustes del servidor. La API de mensajes se ejecutaba como root con un sistema de archivos escribible; ahora funciona con un usuario sin privilegios, sobre un sistema de archivos de solo lectura y sin ninguna capacidad de Linux. Cada respuesta mostraba la versión exacta de nginx, de una rama que ya no recibe correcciones; la versión queda oculta y el contenedor usa la línea estable actual. HSTS, la cabecera que mantiene a los navegadores en HTTPS, se enviaba en el dominio principal pero no en www; ahora cubre www y los subdominios.
Partes del sitio que pasaron la revisión sin cambios
Una auditoría de seguridad web que solo enumera problemas esconde la mitad de su valor. La revisión también confirmó lo que ya estaba bien hecho: la verificación de los tokens de inicio de sesión solo acepta RS256, exige un id de clave y comprueba la audiencia, el emisor y la caducidad; las reglas de Firestore no permiten a nadie leer la conversación de otra persona ni escribir como el propietario; las plantillas de correo escapan cada valor; los registros no contienen tokens, direcciones de correo ni direcciones IP, y el chat nunca inserta como HTML el texto de un visitante.
Tras las correcciones, el conjunto de pruebas de la API pasa 48 de 48 con las nuevas reglas cubiertas, y npm audit no informa de vulnerabilidades conocidas en las dependencias del sitio ni de la API.
Ese mismo día, en la dirección pública, se revisaron una vez más las cabeceras de respuesta: ninguna respuesta muestra la versión del servidor, HSTS con subdominios está activo tanto en el dominio sin www como en www, la política de las demos se aplica en /demos/ y la petición de inyección de cabeceras recibe un simple 404. El catálogo de Oriva en el sitio real compara ahora la categoría con su lista fija antes de usarla, y la biblioteca de mapas se carga con sus hashes de integridad.
Límites de una auditoría hecha por el estudio del propio sitio
Este es el sitio del propio estudio, revisado por el estudio. No es una prueba de penetración independiente ni un certificado, y cubre el código tal como estaba el 28 de septiembre de 2026. El código nuevo, las dependencias nuevas o una demo nueva necesitan otra vez la misma revisión, y por eso una comprobación de seguridad debe formar parte del proceso de publicación y no de un proyecto puntual.
Queda un riesgo estructural, asumido por decisión propia: las demos siguen compartiendo el origen del sitio. La nueva política limita lo que podría alcanzar un código inyectado, pero solo llevar las demos a un dominio aparte las aislaría por completo. Para una pequeña empresa, la lección es práctica: la línea de código más peligrosa suele estar en la página extra olvidada, no en el formulario de inicio de sesión.
Lista práctica
- Enumera cada parte del sitio que ejecuta código: formularios, chats, API, páginas de administración y demos antiguas.
- Comprueba que cada página con inicio de sesión o sesión activa envía una Content-Security-Policy.
- Fija cada script cargado desde un CDN con un hash de integridad o alójalo tú mismo.
- Busca en el código parámetros de la dirección que se escriben en la página como HTML.
- Asegúrate de que HSTS se envía tanto en el dominio sin www como en www, y oculta la versión del servidor.
Preguntas frecuentes
¿Qué es una auditoría de seguridad web de caja blanca?
Es una revisión que se hace con acceso al código fuente y a la configuración del servidor. En lugar de adivinar desde fuera, el revisor lee el código, localiza las debilidades probables y después las reproduce en una copia del entorno real.
¿Por qué una demo conceptual era la parte más arriesgada del sitio?
Las demos funcionan en el mismo origen que el chat, que guarda allí su sesión de inicio de sesión. Por eso, un script inyectado a través de una demo podía actuar dentro de la sesión del propietario, aunque la demo en sí no guarde ningún dato.
¿Una Content-Security-Policy sustituye a la corrección del fallo?
No. La política limita lo que un código inyectado puede cargar o alcanzar, lo que reduce el daño. La inyección en sí sigue teniendo que corregirse, como se hizo aquí aceptando solo valores conocidos.
¿Esta auditoría es una prueba de penetración o un certificado?
Ninguna de las dos cosas. Es la revisión que el estudio hizo de su propio sitio a partir del código fuente, con los ataques reproducidos en una copia local de los contenedores de producción. Una prueba independiente sería un encargo aparte.
¿Cada cuánto debería repetir una web pequeña una revisión así?
Siempre que el código, las dependencias o los scripts de terceros cambien de forma significativa, y como mínimo antes de cada versión importante. Ejecutar npm audit y una comprobación de cabeceras en cada despliegue cubre parte del trabajo de forma automática.
