Resumen

  • El transformador HTML de Cloudflare podía leer más allá de su búfer e insertar fragmentos de otras peticiones en una respuesta, rompiendo el aislamiento entre clientes del mismo borde.
  • Los interruptores de emergencia y el parche detuvieron nuevas filtraciones, pero las copias almacenadas fuera de Cloudflare y las credenciales potencialmente expuestas exigían una recuperación distinta que el parche no podía certificar.

Quien activaba el fallo no era necesariamente la víctima

El 17 de febrero de 2017, Project Zero avisó a Cloudflare de que algunas páginas servidas por su red contenían material ajeno. Había cookies, tokens de autenticación, cuerpos POST y otros fragmentos tomados de la memoria del proceso. Parte de esas respuestas ya figuraba en cachés de buscadores.

El nombre Cloudbleed evoca Heartbleed, pero aquí importa la condición multiinquilino. Un proceso compartido transformaba páginas para muchos clientes. El HTML defectuoso de un sitio podía abrir la ruta vulnerable, mientras que los bytes contiguos volcados a la respuesta procedían de la petición de otro cliente.

Por eso los dominios que dispararon el error no forman un censo de afectados. Un cliente no necesitaba usar la función implicada: bastaba con compartir el proceso equivocado en el instante equivocado. La condición rota no era sólo “analizar bien este documento”, sino “el trabajo de un inquilino nunca lee ni emite la memoria de otro”.

Un cambio de búfer despertó un defecto antiguo

Cloudflare situó el fallo en código antiguo generado con Ragel y utilizado por tres transformaciones: ofuscación de correo, Server-Side Excludes y reescritura automática a HTTPS. Al faltar una operación fhold, un puntero podía quedar al final del búfer. El llamador interpretaba después el resultado como un intervalo válido y copiaba más allá de su límite.

La frecuencia aumentó tras desplegar el parser cf-html, que modificó el modo de fragmentar los búferes. Cloudflare distinguió cuidadosamente causa y contexto: el error no estaba en Ragel ni en cf-html. El componente nuevo cambió las condiciones del código heredado y volvió frecuente un defecto latente.

Esa interacción no aparece en una revisión aislada del componente. Un despliegue prudente debe probar los límites exactos, comparar comportamientos y revisar los fallos de todos los consumidores antiguos que la nueva ruta vuelve a activar.

Una secuencia de apagado con horas exactas

Cloudflare podía ejercer de inmediato el primer control: retirar las funciones que seguían emitiendo memoria. La cronología UTC del informe registra la recepción de los detalles a las 00:32 del 18 de febrero, la desactivación mundial de la ofuscación de correo a la 01:19, la de la reescritura HTTPS a las 04:24 y el despliegue mundial del parche con el interruptor de cf-html a las 07:22. Server-Side Excludes no tenía su propio interruptor global, por lo que hubo que construirlo y desplegarlo.

El interruptor no es una comodidad operativa: pertenece al diseño de seguridad de la función. Sin él se escribe código bajo presión. Con él puede sacrificarse una transformación opcional sin detener el proxy que no depende de ella.

Las claves privadas SSL de los clientes no estuvieron al alcance de esta ruta, según Cloudflare, porque la terminación TLS vivía en otra instancia de NGINX. Sin embargo, la memoria HTTP sí contenía una clave privada usada para cifrar conexiones entre máquinas de Cloudflare y algunos secretos internos de autenticación. El aislamiento redujo el radio de daño; no dejó al proceso sin secretos.

La posterior reescritura en Rust elevó el límite local: el código seguro no permite el mismo acceso fuera de rango. Eso elimina una clase de error en lugar de confiar en que una revisión encuentre cada puntero incorrecto. No demuestra por sí solo una autorización correcta ni protege automáticamente las bibliotecas inseguras.

Una estimación no es una lista completa

El informe inicial concentró la mayor exposición entre el 13 y el 18 de febrero y estimó una petición potencialmente filtrada por cada 3,3 millones, cerca del 0,00003 %. El análisis posterior calculó 1.242.071 activaciones entre el 22 de septiembre de 2016 y el 18 de febrero de 2017: 605.037 antes del 13 de febrero desde menos de 180 sitios y 637.034 durante los cinco días finales desde 6.457 sitios.

La evidencia era limitada. Los registros detallados cubrían sólo una muestra del uno por ciento y estaban disponibles del 8 al 18 de febrero. Los anteriores ya se habían borrado; el período remoto se reconstruyó con analítica agregada y datos de caídas. Cloudflare no encontró indicios de explotación maliciosa en lo que revisó, pero eso no prueba que nadie explotara jamás el fallo.

Una activación no equivale a una víctima, una tasa global no expresa la sensibilidad de un token concreto y una búsqueda negativa en una muestra no cierra el pasado.

El parche no podía recuperar los bytes

Al apagar las funciones cesaron las nuevas respuestas contaminadas, no las copias ya entregadas. Cloudflare estimó que los rastreadores generaron más de la mitad de las activaciones, purgó más de 80.000 páginas únicas y encontró en cachés de terceros datos asociados a 150 clientes.

Ninguna cifra fija el total de afectados. No toda página purgada contenía un secreto; 150 clientes identificados no son un máximo; una purga no demuestra que no haya copias privadas. Contención y recuperación tienen finales distintos.

El parche sólo acredita que la ruta conocida deja de producir nuevas filtraciones bajo las pruebas aplicadas. Los operadores de caché deben borrar su material. Los clientes deben revocar o sustituir sesiones, tokens, claves de API y contraseñas allí donde otorgaban autoridad. La prueba es que el sistema dependiente rechaza la credencial antigua, no que exista una nueva.

Una frontera común mínima y decisiones locales reversibles

La primacía del código en ejecución de Heng Lu exige efectos observables: el borde ya no emite memoria cruzada, el estado de cada función es verificable, los secretos antiguos fracasan y cada dueño de caché trata sus restos. La palabra «resuelto» en una página de estado no produce esos hechos.

La especificación inicial mínima puede ser breve y absoluta: una respuesta sólo contiene datos autorizados para esa respuesta. Las transformaciones opcionales, el parser, el ritmo de despliegue y la retirada son decisiones locales. Pueden cambiar o rechazarse sin debilitar el aislamiento común ni imponer a todos el mismo riesgo.

Cloudbleed no demostró que compartir infraestructura sea inviable. Demostró que compartir requiere una membrana dura y que toda función opcional que la atraviese debe poder desaparecer antes que el servicio completo.

Fuentes