Muchas aplicaciones web necesitan mostrar contenido controlado por el usuario. Esto puede ser tan simple como publicar imágenes subidas por los usuarios (por ejemplo, fotos de perfil) o tan complejo como renderizar HTML controlado por el usuario (por ejemplo, un tutorial de desarrollo web). Siempre fue difícil hacerlo de forma segura, por lo que trabajamos para encontrar soluciones fáciles y 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 publicar contenido controlado por el usuario de forma segura 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, puedes publicar todo el contenido no confiable en exampleusercontent.com. Dado que estos dos dominios son entre sitios, el contenido malicioso de exampleusercontent.com no puede afectar a example.com.
Este enfoque se puede usar para entregar de forma segura todo tipo de contenido no confiable, incluidas imágenes, descargas y HTML. Si bien puede parecer que no es necesario usarlo para imágenes o descargas, hacerlo ayuda a evitar los riesgos del sondeo de contenido, en especial en los navegadores heredados.
Los dominios de zona de pruebas se usan ampliamente 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 la autenticación y la autorización. Dado que los dominios de zona de pruebas no comparten cookies con el dominio principal de la aplicación de forma intencional, es muy difícil hacerlo de forma segura. Para admitir la autenticación, los sitios deben usar URLs de capacidad o establecer cookies de autenticación independientes para el dominio de la zona de pruebas. Este segundo método es especialmente problemático en la Web moderna, donde muchos navegadores restringen las cookies de sitios cruzados de forma predeterminada.
- Si bien el contenido del usuario está aislado del sitio principal, no está aislado de otro contenido del usuario. Esto genera el riesgo de que el contenido malicioso del usuario ataque otros datos en el dominio de zona de pruebas (por ejemplo, leyendo datos del mismo origen).
También vale la pena destacar que los dominios de zona de pruebas ayudan a mitigar los riesgos de phishing, ya que los recursos se segmentan claramente en un dominio aislado.
Soluciones modernas para publicar contenido del usuario
Con el tiempo, la Web evolucionó y ahora existen formas más sencillas y seguras de publicar contenido no confiable. Existen muchos enfoques diferentes, por lo que describiremos dos soluciones que usamos en Google.
Enfoque 1: Entrega de contenido de usuarios inactivos
Si un sitio solo necesita publicar contenido de usuario inactivo (es decir, contenido que no es HTML ni JavaScript, por ejemplo, imágenes y descargas), ahora se puede hacer de forma segura sin un dominio de zona de pruebas aislado. Hay dos pasos clave:
- Siempre establece el encabezado
Content-Typeen un tipo de MIME conocido que sea compatible con todos los navegadores y no contenga contenido activo. Si tienes dudas,application/octet-streames una opción segura. - Además, siempre configura los encabezados de respuesta para garantizar que el navegador aísle por completo la respuesta.
| Encabezado de respuesta | Purpose |
|---|---|
X-Content-Type-Options: nosniff |
Evita la detección de contenido |
Content-Disposition: attachment; filename="download" |
Activa una descarga en lugar de renderizar |
Content-Security-Policy: sandbox |
Aísla el contenido como si se publicara en un dominio independiente. |
Content-Security-Policy: default-src ‘none' |
Inhabilita la ejecución de JavaScript (y la inclusión de cualquier subrecurso). |
Cross-Origin-Resource-Policy: same-site |
Impide que la página se incluya en otros sitios |
Esta combinación de encabezados garantiza que la respuesta solo pueda cargarse como un recurso secundario de tu aplicación o que el usuario la descargue como un archivo. Además, los encabezados proporcionan varias capas de protección contra errores del navegador a través del encabezado de zona de pruebas de la CSP y la restricción default-src. En general, la configuración descrita proporciona un alto grado de confianza en que las respuestas que se publican de esta manera no pueden generar vulnerabilidades de aislamiento o inyección.
Defensa en profundidad
Si bien la solución propuesta representa una defensa generalmente suficiente contra XSS, existen varias medidas de refuerzo adicionales que puedes aplicar para proporcionar capas de seguridad adicionales:
- Establece un encabezado
X-Content-Security-Policy: sandboxpara la compatibilidad con IE11. - Configura un encabezado
Content-Security-Policy: frame-ancestors 'none'para evitar que se incorpore el extremo. - Coloca el contenido del usuario en una zona de pruebas en un subdominio aislado de la siguiente manera:
- Publicar contenido del usuario en un subdominio aislado (p.ej., Google usa dominios como
product.usercontent.google.com). - Establece
Cross-Origin-Opener-Policy: same-originyCross-Origin-Embedder-Policy: require-corppara habilitar el aislamiento de origen cruzado.
- Publicar contenido del usuario en un subdominio aislado (p.ej., Google usa dominios como
Enfoque 2: Entrega de contenido de usuarios activos
También se puede entregar contenido activo de forma segura (por ejemplo, imágenes HTML o SVG) sin las debilidades del enfoque clásico del 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 implementan el aislamiento de procesos para los documentos de zona de pruebas, es probable que las mejoras continuas en los modelos de procesos del navegador mejoren la separación del contenido de zona de pruebas de las aplicaciones de incorporación. Si los ataques de SpectreJS y vulneración del renderizador no se incluyen en tu modelo de amenazas, es probable que usar el espacio aislado de la CSP sea una solución suficiente.
En Google, desarrollamos una solución que puede aislar por completo el contenido activo no confiable modernizando el concepto de dominios de zona de pruebas. La idea principal es la siguiente:
- Crea un nuevo dominio de zona de pruebas que se agregue a la lista de sufijos públicos. Por ejemplo, si agregas
exampleusercontent.coma la PSL, puedes asegurarte de quefoo.exampleusercontent.comybar.exampleusercontent.comsean de sitios cruzados y, por lo tanto, estén completamente aislados entre sí. - Todas las URLs que coinciden con
*.exampleusercontent.com/shimse 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 detecta el controlador de eventosmessagey renderiza cualquier contenido que recibe. - Para usar esto, el producto crea un iframe o un diálogo para
$RANDOM_VALUE.exampleusercontent.com/shimy usapostMessagepara enviar el contenido no confiable al shim para su renderización. - El contenido renderizado se transforma en un objeto 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, como la aplicación principal se encarga de recuperar los datos que se renderizarán, ya no es necesario usar URLs de capacidad.
Conclusión
Juntas, estas dos soluciones permiten migrar de 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.