Aloja datos de usuarios de forma segura en aplicaciones web modernas

David Dworken
David Dworken

Muchas aplicaciones web necesitan mostrar contenido controlado por el usuario. Esto puede ser tan simple como entregar imágenes subidas por el usuario (por ejemplo, fotos de perfil) o tan compleja como renderizar código HTML controlado por el usuario (por ejemplo, un instructivo de desarrollo web). Esto siempre ha sido difícil de hacer de forma segura, por lo que trabajamos para encontrar soluciones fáciles, pero seguras, que se puedan aplicar a la mayoría de los tipos de aplicaciones web.

Soluciones clásicas para aislar contenido no confiable

La solución clásica para entregar de forma segura contenido controlado por el usuario es usar lo que se conoce como dominios de zona de pruebas. La idea básica es que, si el dominio principal de tu aplicación es example.com, podrías entregar todo el contenido que no sea de confianza en exampleusercontent.com. Como estos dos dominios son entre sitios, el contenido malicioso en exampleusercontent.com no puede afectar a example.com.
Este enfoque se puede usar para publicar de forma segura todo tipo de contenido no confiable, como imágenes, descargas y HTML. Aunque no parezca necesario usarlo para imágenes o descargas, hacerlo ayuda a evitar los riesgos de sniffing de contenido, especialmente en navegadores heredados.
Los dominios de las zonas de pruebas son muy utilizados en la industria y han funcionado bien durante mucho tiempo. Sin embargo, tienen dos desventajas importantes:

  • A menudo, las aplicaciones necesitan restringir el acceso al contenido a un solo usuario, lo que requiere implementar autenticación y autorización. Debido a que los dominios de las zonas de pruebas no comparten cookies a propósito con el dominio principal de la aplicación, esto es muy difícil de hacer de forma segura. Para admitir la autenticación, los sitios deben depender de las URLs de capacidad o deben configurar cookies de autenticación independientes para el dominio de la zona de pruebas. Este segundo método es particularmente problemático en la Web moderna, donde muchos navegadores restringen las cookies entre sitios de forma predeterminada.
  • Si bien el contenido del usuario está aislado del sitio principal, no del resto del contenido. Esto genera el riesgo de que el contenido malicioso del usuario ataque otros datos en el dominio de la zona de pruebas (por ejemplo, a través de la lectura de datos del mismo origen).

También vale la pena señalar que los dominios de zona de pruebas ayudan a mitigar los riesgos de phishing, ya que los recursos están claramente segmentados en un dominio aislado.

Soluciones modernas para entregar contenido a los usuarios

Con el tiempo, la Web ha evolucionado y ahora existen formas más fáciles y seguras de entregar contenido que no es de confianza. Aquí hay muchos enfoques diferentes, por lo que describiremos dos soluciones que actualmente se utilizan ampliamente en Google.

Enfoque 1: Publicación de contenido de usuarios inactivos

Si un sitio solo necesita publicar contenido de usuarios inactivos (es decir, contenido que no es HTML o JavaScript, como imágenes y descargas), ahora esto se puede hacer de forma segura sin un dominio de zona de pruebas aislado. Hay dos pasos clave:

  • Siempre debes configurar el encabezado Content-Type con un tipo de MIME conocido que sea compatible con todos los navegadores y que no incluya contenido activo (en caso de dudas, application/octet-stream es una opción segura).
  • Además, establece siempre los siguientes encabezados de respuesta para asegurarte de que el navegador aísle por completo la respuesta.
Encabezado de respuesta Purpose

X-Content-Type-Options: nosniff

Evita el análisis de contenido

Content-Disposition: attachment; filename="download"

Activa una descarga en lugar de la renderización.

Content-Security-Policy: sandbox

Permite una zona de pruebas del contenido como si se entregara en un dominio independiente.

Content-Security-Policy: default-src ‘none'

Inhabilita la ejecución de JavaScript (y la inclusión de subrecursos).

Cross-Origin-Resource-Policy: same-site

Impide que la página se incluya entre sitios

Esta combinación de encabezados garantiza que la respuesta solo pueda ser cargada como un subrecurso por tu aplicación o descargada como un archivo por el usuario. Además, los encabezados proporcionan varias capas de protección contra errores del navegador a través del encabezado de la zona de pruebas de la CSP y la restricción default-src. En general, la configuración descrita anteriormente proporciona un alto nivel de confianza en que las respuestas entregadas de esta manera no pueden generar vulnerabilidades de inserción o aislamiento.

Defensa en profundidad

Si bien la solución anterior representa una defensa generalmente suficiente contra XSS, hay una serie de medidas de endurecimiento adicionales que puedes aplicar para proporcionar capas adicionales de seguridad:

  • Establece un encabezado X-Content-Security-Policy: sandbox para que sea compatible con IE11.
  • Establece un encabezado Content-Security-Policy: frame-ancestors 'none' para evitar que se incorpore el extremo.
  • Haz una zona de pruebas del contenido del usuario en un subdominio aislado de la siguiente manera:
    • Entregar contenido de usuarios en un subdominio aislado (p. ej., Google usa dominios como product.usercontent.google.com)
    • Configura Cross-Origin-Opener-Policy: same-origin y Cross-Origin-Embedder-Policy: require-corp para habilitar el aislamiento de origen cruzado.

Enfoque 2: Entrega de contenido de usuarios activos

La entrega segura de contenido activo (por ejemplo, imágenes HTML o SVG) también se puede realizar sin las debilidades del enfoque clásico de dominio de zona de pruebas.
La opción más simple es aprovechar el encabezado Content-Security-Policy: sandbox para indicarle al navegador que aísle la respuesta. Si bien no todos los navegadores web actualmente implementan el aislamiento de procesos para los documentos de la zona de pruebas, es probable que las mejoras continuas en los modelos de procesos del navegador mejoren la separación del contenido de la zona de pruebas de las aplicaciones incrustadas. Si los ataques de SpectreJS y el compromiso del procesador están fuera de tu modelo de amenaza, es probable que usar la zona de pruebas de CSP sea una solución suficiente.
En Google, desarrollamos una solución que puede aislar por completo el contenido activo no confiable mediante la modernización del concepto de dominios de zona de pruebas. La idea central es:

  • Crea un nuevo dominio de zona de pruebas que se agregue a la lista de sufijos públicos. Por ejemplo, si agregas exampleusercontent.com a la PSL, puedes asegurarte de que foo.exampleusercontent.com y bar.exampleusercontent.com sean entre sitios y, por lo tanto, estén completamente aislados entre sí.
  • Las URLs que coinciden con *.exampleusercontent.com/shim se enrutan a un archivo de corrección de compatibilidad estático. Este archivo de corrección de compatibilidad contiene un fragmento corto de HTML y JavaScript que escucha el controlador de eventos message y renderiza el contenido que recibe.
  • Para usar esto, el producto crea un iframe o una ventana emergente en $RANDOM_VALUE.exampleusercontent.com/shim y usa postMessage para enviar el contenido que no es de confianza a la corrección de compatibilidad para su renderización.
  • El contenido renderizado se transforma en un BLOB y se renderiza dentro de un iframe de zona de pruebas.

En comparación con el enfoque clásico de dominio de zona de pruebas, esto garantiza que todo el contenido esté completamente aislado en un sitio único. Además, si la aplicación principal se encarga de recuperar los datos que se procesarán, ya no es necesario usar URLs de funciones.

Conclusión

Juntas, estas dos soluciones permiten migrar de los dominios de zona de pruebas clásicos, como googleusercontent.com, a soluciones más seguras que son compatibles con el bloqueo de cookies de terceros. En Google, ya migramos muchos productos para usar estas soluciones y tenemos más migraciones planificadas para el próximo año.