Request smuggling y desincronización HTTP:
cuando el proxy y el servidor no se ponen de acuerdo
Seguridad de aplicaciones web | unfantasmaenelsistema.com
Casi ninguna aplicación web habla directamente con el navegador. Entre medias hay uno o varios intermediarios: un balanceador, un CDN, un proxy inverso, un WAF, una pasarela de API. El navegador manda una petición al primero de la cadena (el front-end), este la reenvía al siguiente y así hasta el servidor de aplicación (el back-end), que es quien realmente la procesa. Para ahorrar recursos, esos saltos suelen reutilizar una misma conexión TCP para muchas peticiones seguidas, una detrás de otra.
El request smuggling —o desincronización HTTP— aparece cuando el front-end y el back-end no interpretan igual dónde termina una petición y dónde empieza la siguiente dentro de ese flujo compartido. Basta esa discrepancia para que un atacante logre que parte de lo que él envía quede «pegado» al principio de la petición de otra persona. Este artículo explica el mecanismo, qué permite, cómo se detecta y cómo se elimina; sin recetas para atacar servicios ajenos.
Que aprenderas en este articulo
Como el mismo mensaje HTTP puede parecer ‘una peticion’ para el front-end y ‘dos peticiones’ para el back-end · Las familias clasicas: CL.TE, TE.CL y TE.TE, y por que HTTP/1.1 las hace posibles · Las variantes modernas: downgrade de HTTP/2 a HTTP/1.1, request splitting y H2.CL / H2.TE · Que consigue un atacante: envenenar la cola de respuestas, saltarse controles del front y capturar peticiones ajenas · Deteccion (anomalias de longitud y de temporizacion) y defensa definitiva (rechazar lo ambiguo, HTTP/2 extremo a extremo)
Encuadre legal y de pruebas
Prueba estas tecnicas unicamente contra laboratorios propios o deliberadamente vulnerables (los labs de HTTP request smuggling de PortSwigger Web Security Academy, una cadena front-end/back-end que montes tu mismo en el rango 192.168.56.x de la serie) o con autorizacion escrita del titular · Enviar peticiones manipuladas contra una infraestructura ajena para desincronizar su cola de peticiones puede constituir acceso ilicito a sistemas de informacion, interceptacion de datos y danos informaticos (arts. 197 bis, 197 ter y 264 del Codigo Penal espanol) · El request smuggling afecta a peticiones de otros usuarios reales: incluso una prueba ‘inocente’ en produccion ajena puede degradar el servicio o exponer datos de terceros. Si encuentras un caso, no lo explotes: documenta lo minimo y usa el canal de divulgacion responsable del proveedor o el INCIBE-CERT
1. El origen: dos formas de decir dónde acaba una petición
En HTTP/1.1 el cuerpo de una petición puede delimitarse de dos maneras distintas. La cabecera Content-Length indica en bytes cuánto ocupa el cuerpo. La cabecera Transfer-Encoding: chunked dice que el cuerpo llega en trozos, cada uno precedido por su tamaño en hexadecimal, y que termina con un trozo de tamaño cero. Ambos mecanismos son válidos, pero el estándar prohíbe usar los dos a la vez en la misma petición precisamente porque son ambiguos juntos.
El problema es qué hace cada servidor cuando llega una petición que, por descuido o por malicia, incluye las dos cabeceras o una versión rara de una de ellas. Si el front-end decide fiarse de Content-Length y el back-end de Transfer-Encoding (o al revés), cada uno leerá un número diferente de bytes como «la primera petición». Lo que uno considera final del mensaje, el otro lo considera principio del siguiente. A partir de ahí la conexión reutilizada queda desincronizada: todas las peticiones posteriores de esa conexión se interpretan corridas una posición.
| Familia | Front-end usa | Back-end usa | Efecto |
|---|---|---|---|
| CL.TE | Content-Length | Transfer-Encoding: chunked | El back-end cierra el cuerpo antes; los bytes sobrantes quedan como inicio de la siguiente peticion |
| TE.CL | Transfer-Encoding: chunked | Content-Length | El front-end reenvia mas de lo que el back-end considera cuerpo; el resto se procesa como peticion nueva |
| TE.TE | Transfer-Encoding (ofuscado) | Transfer-Encoding (ofuscado) | Ambos soportan chunked pero uno ignora una version malformada de la cabecera y cae a Content-Length |
Por que ‘chunked’ es el ingrediente habitual
Un front-end que normaliza todo a Content-Length y reenvia, combinado con un back-end que sigue honrando Transfer-Encoding, es la receta de la mayoria de casos CL.TE · Las variantes TE.TE se consiguen escribiendo la cabecera de forma no estandar (espacios raros, mayusculas/minusculas mezcladas, un caracter de mas) para que solo uno de los dos servidores la reconozca · La defensa no es ‘detectar la ofuscacion concreta’: es que un unico componente decida y que cualquier peticion ambigua se rechace de plano
2. Qué se consigue con la desincronización
La desincronización en sí no roba nada; lo que importa es qué se puede hacer con ese hueco al principio de la petición de la víctima. Las consecuencias habituales, descritas a alto nivel:
- Saltarse controles del front-end. Si el front-end aplica autenticacion, listas de acceso o reglas de WAF y el back-end confia en que ya vienen filtradas, un fragmento ‘contrabandeado’ llega al back-end sin pasar por esos controles y alcanza rutas internas que deberian estar vetadas.
- Envenenar la cola de respuestas (response queue poisoning). El atacante consigue que las respuestas se desplacen: la victima recibe la respuesta destinada a otra peticion, y viceversa. En el peor caso se sirve a un usuario contenido de la sesion de otro.
- Capturar peticiones de otros usuarios. El prefijo contrabandeado hace que la peticion completa de la victima (con sus cookies, cabeceras de sesion o tokens) quede almacenada como si fuera un dato enviado por el atacante, que luego la recupera.
- Envenenamiento de cache. Combinado con una cache intermedia, una respuesta manipulada se almacena y se sirve a todo el que pida esa URL despues.
- Amplificar otras debilidades. Un XSS que requeria interaccion se vuelve explotable ‘a ciegas’ contra quien pase por ahi; un endpoint interno de administracion queda expuesto.
El rasgo que hace peligroso al request smuggling es que no ataca al atacante: ataca a los usuarios legítimos que comparten esa conexión aguas arriba. Por eso ni siquiera está justificado «probar un poco» contra un sistema de terceros.
3. HTTP/2 no lo soluciona solo: el problema del downgrade
HTTP/2 define la longitud de cada mensaje con un mecanismo propio (frames con longitud explícita), así que la ambigüedad Content-Length vs Transfer-Encoding no existe entre dos extremos que hablen HTTP/2 puro. El problema aparece cuando el front-end acepta HTTP/2 del cliente pero lo traduce a HTTP/1.1 para hablar con el back-end, algo muy común.
En esa traducción, el front-end tiene que reconstruir una petición HTTP/1.1 a partir de la de HTTP/2. Si no valida bien el contenido de las pseudo-cabeceras y las cabeceras del mensaje HTTP/2, un atacante puede colar saltos de línea o cabeceras de longitud dentro de un valor, y el texto HTTP/1.1 resultante contiene una segunda petición incrustada. Es el mismo efecto que CL.TE/TE.CL pero introducido en el momento de la conversión.
| Variante | Situacion | Idea general |
|---|---|---|
| H2.CL | HTTP/2 -> HTTP/1.1 con Content-Length inyectada | El atacante declara un Content-Length en HTTP/2 que el back-end HTTP/1.1 honra y usa para recortar el cuerpo |
| H2.TE | HTTP/2 -> HTTP/1.1 con Transfer-Encoding inyectada | Se cuela Transfer-Encoding: chunked al bajar de version y el back-end la aplica |
| Request splitting por CRLF | Valores de cabecera HTTP/2 sin sanear | Un salto de linea CR LF dentro de un valor parte el mensaje HTTP/1.1 en dos al serializarlo |
| HTTP/2 puro extremo a extremo | Front-end y back-end hablan HTTP/2 sin downgrade | No hay ambiguedad de longitud: es la configuracion recomendada |
La regla practica sobre HTTP/2
HTTP/2 solo cierra el problema si se mantiene de extremo a extremo, sin bajar a HTTP/1.1 en ningun salto interno · Si hay downgrade, el front-end DEBE rechazar toda cabecera y pseudo-cabecera HTTP/2 con caracteres de control (CR, LF, NUL) y no permitir que el cliente fije Content-Length ni Transfer-Encoding · Muchos casos reales de 2019-2023 fueron precisamente downgrades mal validados en CDNs y balanceadores
4. Cómo se detecta
4.1 Desde fuera, con pruebas controladas
En un pentest autorizado o en tu propio laboratorio, la detección se apoya en dos señales observables sin necesidad de ver el interior de la infraestructura:
- Diferencia de temporizacion. Se envia una peticion construida para que, SI existe desincronizacion, el back-end se quede esperando bytes que nunca llegan. Un retardo claro y reproducible frente a una peticion normal es el primer indicio (tecnica de deteccion por tiempo).
- Respuestas anomalas a la peticion siguiente. Tras la peticion sospechosa se manda otra normal por la misma conexion; si vuelve un codigo de estado o un cuerpo que no corresponde (un 404 donde deberia haber 200, una respuesta partida, cabeceras duplicadas), la cola esta desincronizada.
- Herramientas de test propio. La extension HTTP Request Smuggler de Burp Suite y el modo de estudio de la Web Security Academy automatizan estas sondas contra objetivos que controlas. Ejecutarlas contra terceros sin permiso es lo que convierte la prueba en delito.
4.2 Desde dentro, en los logs y la telemetría
El defensor tiene ventaja: ve los dos lados de la conversación. Señales que un SIEM o una revisión de logs pueden recoger (enlaza con los artículos de Sigma y Wazuh de la serie):
- Peticiones con Content-Length y Transfer-Encoding a la vez, o con Transfer-Encoding escrita de forma no estandar, en los logs del front-end o del WAF.
- Desajuste entre el numero de peticiones que registra el front-end y las que registra el back-end para una misma conexion o en una misma ventana de tiempo.
- Respuestas del back-end cuyo tamano o codigo no casa con ninguna peticion registrada; lineas de log de aplicacion con metodos o rutas ‘raras’ pegadas al final del cuerpo de otra peticion.
- Acceso a rutas internas o de administracion desde la IP del front-end pero sin la traza de autenticacion que ese front-end deberia haber anadido.
- Picos de errores 400/408/502 concentrados, tipicos de sondas de smuggling que dejan la conexion a medias.
# Lectura/auditoria: localizar peticiones con doble delimitador de longitud # en un log de acceso que registre cabeceras (formato propio del proxy). grep -iE 'transfer-encoding' access.log | grep -iE 'content-length' # Transfer-Encoding con espacios o mayusculas sospechosas antes del valor grep -iE 'transfer-encoding[^:]*:[^c]*chunked' access.log # Nginx: exigir que se rechace la peticion ambigua (comportamiento por defecto # en versiones actuales; verificar tras cada actualizacion). # Directiva relevante: ignore_invalid_headers on; (valor por defecto) # Revisar que el back-end NO reutiliza una conexion tras una peticion malformada: # Apache httpd: comprobar 'HttpProtocolOptions Strict' (por defecto desde 2.4.24)
5. Defensa: eliminar la ambigüedad
El request smuggling no se «mitiga» con parches parciales: se elimina cuando toda la cadena interpreta cada petición de la misma forma. Las medidas, de más a menos determinante:
| Medida | Que consigue | Nota |
|---|---|---|
| Rechazar toda peticion ambigua | Si llegan Content-Length y Transfer-Encoding juntas, o una cabecera de longitud malformada, se responde 400 y se cierra la conexion | Es la defensa central; la mayoria de servidores modernos ya lo hacen por defecto, hay que confirmarlo |
| HTTP/2 de extremo a extremo | Elimina la ambiguedad de longitud en todos los saltos internos | Requiere que el back-end y los proxies internos hablen HTTP/2; si no, validar el downgrade a fondo |
| Normalizar en un unico punto | Un solo componente decide la forma canonica de la peticion y todos los demas la respetan | Evita que front-end y back-end ‘negocien’ cada uno por su lado |
| No reutilizar la conexion aguas arriba | Sin conexion compartida no hay cola que desincronizar (una conexion por peticion al back-end) | Cuesta rendimiento; util como medida temporal mientras se corrige la cadena |
| Mismo software y misma config en la cadena | Dos servidores identicos no discrepan sobre donde acaba una peticion | No siempre es posible (CDN + origen), pero reduce la superficie |
| Mantener actualizados proxy, WAF y servidor | La mayoria de variantes conocidas se corrigieron en versiones concretas | Verificar el comportamiento tras cada actualizacion, no darlo por hecho |
| WAF con reglas anti-smuggling | Filtra cabeceras duplicadas y Transfer-Encoding ofuscada | Capa extra, no sustituto: el WAF tambien puede ser el componente que discrepa |
Checklist rápida para una cadena front-end / back-end
Confirma que el front-end rechaza (no ‘normaliza y reenvia’) cualquier peticion con doble delimitador de longitud · Comprueba que el back-end no acepta Transfer-Encoding de una fuente que no deberia usarla y que no reutiliza conexiones tras un error de protocolo · Si hay downgrade de HTTP/2 a HTTP/1.1, revisa que se saneen CR/LF/NUL en cabeceras y pseudo-cabeceras y que el cliente no pueda fijar Content-Length ni Transfer-Encoding · Ejecuta las sondas de deteccion contra tu propio entorno de preproduccion tras cada cambio de infraestructura
6. Practicar en laboratorio
Para entender el mecanismo de primera mano hace falta una cadena con al menos dos servidores que discrepen. La forma más limpia de estudiarlo sin tocar nada ajeno:
- Los laboratorios de ‘HTTP request smuggling’ de la PortSwigger Web Security Academy: son gratuitos, estan pensados para esto y cubren CL.TE, TE.CL, TE.TE, las variantes de HTTP/2 y los escenarios de explotacion (bypass de controles, captura de peticiones, envenenamiento de cache).
- Un montaje propio en el rango 192.168.56.x de la serie: por ejemplo un proxy inverso delante de un servidor de aplicacion sencillo, deliberadamente con versiones o configuraciones que discrepen, solo para observar la desincronizacion en los logs de ambos.
- Del lado defensivo: reproduce las sondas y escribe la regla de deteccion (Sigma) que las cazaria en el log del front-end; comprueba que una configuracion endurecida devuelve 400 y cierra la conexion.
- No uses estas pruebas contra servicios en produccion, tuyos o ajenos: afectan a la cola de peticiones real y pueden alcanzar a usuarios que no son tu.
Conclusión
El request smuggling es un fallo de acuerdo, no de código de aplicación: dos piezas de infraestructura que leen el mismo mensaje y cuentan un número distinto de bytes. Esa grieta minúscula basta para que lo que envía un atacante se adhiera a la petición de otra persona, con consecuencias que van desde saltarse el WAF hasta servir la sesión de un usuario a otro.
La buena noticia es que la solución es conceptualmente simple y definitiva: que un único componente decida la forma canónica de cada petición, que cualquier mensaje ambiguo se rechace en lugar de «arreglarse», y que HTTP/2 se mantenga de extremo a extremo siempre que se pueda. Con la cadena alineada, no queda desincronización que explotar.
Referencias y recursos
PortSwigger – ‘HTTP request smuggling’ (articulo de investigacion de James Kettle) y laboratorios de la Web Security Academy · PortSwigger – ‘HTTP/2: The Sequel is Always Worse’ (downgrade, H2.CL, H2.TE, request splitting) · OWASP – HTTP Request Smuggling y OWASP Testing Guide (WSTG-INPV-15) · CWE-444: Inconsistent Interpretation of HTTP Requests (‘HTTP Request/Response Smuggling’) · RFC 9112 (HTTP/1.1) – seccion sobre message body length y el conflicto Content-Length / Transfer-Encoding · Documentacion de tu proxy (nginx, HAProxy, Apache httpd, Envoy) – opciones de ‘strict’ parsing de HTTP
unfantasmaenelsistema.com | Seguridad, sistemas y todo lo que no debería funcionar (pero funciona)

