graygnuorg / pound

Light-weight reverse proxy, load balancer and HTTPS front-end for Web servers.
GNU General Public License v3.0
43 stars 13 forks source link

Bug in chunked message body decoding causes malformed requests to be forwarded #28

Closed kenballus closed 3 months ago

kenballus commented 3 months ago

When Pound receives a request with the Transfer-Encoding: chunked header and a malformed message body, Pound forwards that request without its message body.

To see this for yourself, send Pound the following request:

POST /  HTTP/1.1\r\n
Transfer-Encoding: chunked\r\n
\r\n
1\r0\n

It should forward something like this:

POST /  HTTP/1.1\r\n
Transfer-Encoding: chunked\r\n
X-Forwarded-For: <some ip address>\r\n
X-Forwarded-Proto: http\r\n
X-Forwarded-Port: <some port>\r\n
\r\n

This forwarded message is incomplete, and will almost certainly cause the backend to time out while it waits for the message body to arrive.

graygnuorg commented 3 months ago

Yes, any incomplete chunked message is forwarded as such. For what it's worth, backend answers immediately with 400. Checked with apache2.

kenballus commented 3 months ago

Apache is responding 400 because the example request I sent didn't have a Host header. If you add a Host header, then Apache will time out.

But to be clear, it's not a problem if incomplete message bodies are forwarded. The issue is that invalid message bodies are not rejected. The message body above contains an invalid chunk, so it should definitely get a 400 from Pound.

kenballus commented 3 months ago

Pound is the only HTTP server I know of that accepts the above request.

All of the following either reject the request with 400 or time out:

aiohttp, apache, cheroot, daphne, deno, fasthttp, go net/http, gunicorn, h2o, haproxy, hyper, hypercorn, jetty, libevent, libsoup, lighttpd, mongoose, netty, nginx, nodejs, openlitespeed, passenger, tomcat, tornado, uhttpd, unicorn, uvicorn, waitress, webrick, werkzeug, openbsd httpd, apache traffic server, nghttpx, squid, varnish, akamai, aws cloudfront, cloudflare, fastly, google classic app. LB, Envoy, and relayd.

graygnuorg commented 3 months ago

Apache is responding 400 because the example request I sent didn't have a Host header. If you add a Host header, then Apache will time out.

It goes without saying that I added the Host header before trying out the request.

kenballus commented 3 months ago

It goes without saying that I added the Host header before trying out the request.

Just making sure.

Apache will 400 if the sender half-closes the TCP connection. Otherwise, it waits for the sender to send the rest of the request.