Engaño al caché web (WCD): cómo se produce y cómo evitarlo

Engaño al caché web (WCD): cómo se produce y cómo evitarlo

Web Cache Deception (WCD) — es una vulnerabilidad de configuración en la infraestructura de caché, en la que una respuesta personalizada de la aplicación se guarda por error y se entrega como un objeto estático común. Como resultado, el contenido dependiente de la sesión de usuario se vuelve accesible sin autenticación al solicitarse por la misma dirección.

Causa

La caché en el borde de la red decide almacenar según la clave de caché y las reglas de emparejamiento. Si la política se basa en la extensión de archivo o en patrones de ruta, en lugar de en las cabeceras de respuesta y el contexto de autenticación, el HTML dinámico puede ser tratado como un recurso estático. Adicionalmente, el riesgo aumenta cuando hay reescritura de rutas a nivel de proxy o del framework, cuando distintas variantes de URL son manejadas por el mismo controlador.

Secuencia de explotación

  1. Manipulación de la ruta. Un atacante construye una dirección como /account/statement.css, aunque el controlador atiende /account. El enrutador ignora el sufijo y devuelve la página personal.
  2. Clasificación incorrecta de la respuesta. La caché perimetral aplica la política correspondiente a un recurso estático por la extensión y guarda la respuesta sin considerar cookies ni la cabecera Authorization.
  3. Acceso repetido sin autenticación. Cualquier cliente solicita la misma dirección y recibe la respuesta personalizada previamente almacenada en caché.

Condiciones en la infraestructura

  • Reescritura de la ruta. Un solo controlador atiende /private y /private/cola-aleatoria.jpg.
  • Heurísticas por extensión. Las reglas de caché están ligadas a patrones de extensión sin verificar el contexto de autenticación.
  • Falta de saneamiento por autorización. El sistema de caché no excluye del almacenamiento respuestas que dependen de Cookie o Authorization.
  • Clave de caché incorrecta. La clave no considera parámetros que afectan la personalización y no está ligada a cabeceras significativas.
  • Cabeceras de respuesta erróneas. En rutas personales faltan las directivas Cache-Control no-store o private y un conjunto correcto de cabeceras Vary.

Indicadores en la telemetría

  • Proporción excesivamente alta de aciertos de caché en prefijos donde las respuestas dependen de la sesión del usuario.
  • Aumento de solicitudes a direcciones con extensiones css js jpg pdf dentro de secciones privadas.
  • Incompatibilidad del tipo de contenido en respuestas desde caché, por ejemplo Content-Type: text/html para rutas con extensión de recurso estático.
  • Reducción de la carga en el origen al aumentar las visitas a páginas privadas.

Diferencia con Web Cache Poisoning

En Web Cache Poisoning la caché almacena una respuesta manipulada. En Web Cache Deception la caché almacena una respuesta correcta en su contenido pero personalizada, que no debería haber entrado en la capa compartida.

Política correcta de cabeceras

  • Cache-Control. Para páginas personales use no-store o private con no-cache. No confíe en el comportamiento por defecto.
  • Vary. Si el contenido depende de Cookie, Authorization, locale o geografía, liste esos parámetros en la cabecera Vary. Ejemplo: Vary: Cookie, Authorization, Accept-Language.
  • Clave de caché explícita. Forme la clave a partir de la ruta normalizada y un conjunto limitado de parámetros y cabeceras que realmente influyen en el cuerpo de la respuesta.
  • Caché negativo. TTL corto para 404 y 403 en secciones privadas reduce la carga en el origen durante intentos de enumeración de rutas.

Errores comunes de configuración

  • Expresiones regulares de enrutamiento únicas para rutas privadas y estáticas.
  • Caché por patrones de extensión dentro de prefijos privados.
  • Falta de directivas Cache-Control en controladores personales.
  • Clave de caché que no considera Cookie ni Authorization.
  • Falta de normalización de rutas y lista blanca de extensiones para la estática cacheable.

Recomendaciones de arquitectura

Normalización de rutas y separación de contornos

  • Servir lo estático desde un prefijo o subdominio separado, atendido desde un origen independiente.
  • Los controladores privados deben aceptar rutas estrictamente fijas. Cualquier intento de añadir una extensión a una ruta privada debe bloquearse en el borde antes de llegar a la aplicación.
  • Usar lista blanca de extensiones y reglas explícitas de caché solo para el prefijo estático.

Saneamiento de caché según autorización

  • Si la solicitud contiene Authorization o una cookie de sesión, no debe ocurrir caché y la respuesta de la aplicación debe incluir Cache-Control: no-store.
  • A nivel de CDN y proxy inverso habilitar el bypass de caché al detectar signos de autenticación.

Formación de la clave de caché

  • Incluir en la clave solo la ruta normalizada y una lista limitada de parámetros de consulta que afecten al contenido, por ejemplo un parámetro de versión del recurso.
  • Excluir parámetros ruidosos y cabeceras inestables.

Caché negativo y evitar heurísticas

  • Configurar TTL corto para códigos 404 y 403 en secciones privadas.
  • Prohibir reglas del tipo "si la extensión es jpg entonces cachear" dentro de prefijos privados.

Ejemplos de configuración en forma de pseudocódigo

Bypass de caché al detectar autorización

# edge o proxy inverso if request.headers.contains("Authorization") or request.cookies.contains("session") then cache.bypass() response.headers.set("Cache-Control","no-store") end 

Lista blanca de estática

# cache solo bajo /static y solo extensiones permitidas if path.starts_with("/static/") and ext in [".css",".js",".png",".jpg",".svg",".woff2"] then cache.allow(ttl="24h", key=normalize(path, query=["v"])) else cache.bypass() end 

Protección de rutas privadas contra la adición de extensiones

# bloquear intentos de acceder a páginas privadas con sufijo de extensión if path.starts_with("/account/") and path.matches(".(css|js|png|jpg|svg|pdf)$") then return 404 end 

Cabeceras de respuesta para HTML personal

HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 Cache-Control: no-store Vary: Cookie, Authorization, Accept-Language 

Observabilidad y control

  • Registros de caché con campos: clave de caché, indicador de acierto, motivo de la decisión e información de autorización.
  • Alerta ante un aumento brusco de aciertos de caché en prefijos privados.
  • Alerta ante la discrepancia entre Content-Type y el tipo esperado según la extensión de la ruta.
  • Trazado con etiqueta de origen de la respuesta: caché u origen para prefijos privados.

Procedimiento de respuesta

Primeros minutos

  • Habilitar bypass de caché para los prefijos privados account, user, admin.
  • Desactivar reglas de caché basadas en extensiones dentro de prefijos privados.
  • Reducir el TTL del caché público a un valor seguro y habilitar revalidate.

Primera hora

  • Extraer estadísticas de aciertos de caché por rutas privadas y correlacionarlas con la presencia de signos de autorización.
  • Habilitar caché negativo para 404 y 403 y normalizar rutas.
  • Publicar actualizaciones de los controladores con Cache-Control: no-store y un conjunto correcto de Vary.

Primeras 24 horas

  • Separar estático y dinámico en dominios o zonas de caché diferentes.
  • Añadir pruebas unitarias e integradas que simulen accesos a secciones privadas con máscaras de extensión y verifiquen el comportamiento de la caché para un cliente anónimo.
  • Documentar la matriz de políticas de caché para cada prefijo.

Comprobaciones automáticas

En la canalización de despliegue conviene añadir una prueba con dos clientes. El primer cliente autentica y recibe una respuesta en una dirección como /account/detail.jpg registrando cabeceras y el origen de la respuesta. El segundo cliente sin autenticación solicita la misma dirección. La prueba falla si el segundo cliente recibe contenido distinto de 404, 403 o una redirección a la página de inicio de sesión.


Alt text