Long post incoming! Both nginx and haproxy do not care whether double-quoted strings are closed in chunk extensions; they just scan forward until hitting a line ending. As far as I can determine, this is not exploitable behavior with haproxy pointing at nginx or nginx pointing at haproxy in any configuration.
Thus, I believe Rajat is mistaken in two ways: 1. His claim that the RFC says that dquoted-CRLFs are allowable is wrong. The RFC says CR and LF cannot appear within a chunk-ext at all. 2. His claim that nginx allows dquoted-CRLFs in chunk-exts is wrong. Nginx does the same thing as haproxy. Two questions remain to be answered: 1. Might this behavior be exploitable when haproxy is composed with some web server other than nginx? 2. Should haproxy change its current behavior? First, question 1. I believe that if there were a proxy server that did behave the way Rajat claimed that nginx does, you could do request smuggling if that server were forwarding requests to haproxy. (i.e., the inverse of the server chain Rajat provided). An example payload to exploit this discrepancy might look like this: (formatted for the hypothetical proxy's perspective) ``` POST / HTTP/1.1\r\n Host: a\r\n Transfer-Encoding: chunked\r\n \r\n 0;ext="\r\n\r\nGET /evil HTTP/1.1\r\nX:"\r\n \r\n ``` (and now formatted for haproxy's perspective) ``` POST / HTTP/1.1\r\n Host: a\r\n Transfer-Encoding: chunked\r\n \r\n 0;ext="\r\n \r\n GET /evil HTTP/1.1\r\n X:"\r\n \r\n ``` I dusted off the HTTP Garden to see if any suitable proxy server exists, and as far as I can tell, one doesn't. Pound is very, very close though. Pound will forward CRLFs inside of chunk-extensions, but won't forward 2 of them in a row. This is suitable to get trailer injection, but not enough for request smuggling, unless there's some creative thing I'm not thinking of :) To do the trailer injection, the payload looks like this for a `client <-> pound <-> haproxy <-> ...` server chain: Client sends this to pound: (text is formatted for pound's perspective) ``` POST / HTTP/1.1\r\n Host: a\r\n Transfer-Encoding: chunked\r\n \r\n 0;ext="\r\nTrailer-not-interpreted-by-pound: value"\r\n \r\n ``` Pound forwards this to haproxy: (text is formatted for haproxy's perspective) ``` 'POST / HTTP/1.1\r\n Host: a\r\n Transfer-Encoding: chunked\r\n X-Forwarded-For: 172.18.0.1\r\n X-Forwarded-Proto: http\r\n X-Forwarded-Port: 80\r\n \r\n 0;ext="\r\n Trailer-not-interpreted-by-pound: value"\r\n \r\n' ``` Haproxy then forwards this to the next server in the chain: ``` POST / HTTP/1.1\r\n host: a\r\n transfer-encoding: chunked\r\n x-forwarded-for: 172.18.0.1\r\n x-forwarded-proto: http\r\n x-forwarded-port: 80\r\n \r\n 0\r\n trailer-not-interpreted-by-pound: value"\r\n \r\n ``` So I would say haproxy's behavior is indeed exploitable, but only when a proxy with a much more egregious spec violation (Pound) is between haproxy and the client. As for question 2, I lean toward fully validating chunk extensions. If we claim to implement a protocol, we should strive to do so completely and correctly when possible. It seems unlikely to me that this would have an unacceptable performance cost, because almost no one uses chunk extensions anywway, so any validation code would probably run pretty infrequently. For 99% of requests, this should cost ~1 predicted branch. Of course, theorizing about performance is super hard, so I could easily be wrong about the cost of the extra validation. Whether you think the spec requires this is I think this is a matter of taste. RFC 9112 says this: > When a server listening only for HTTP request messages, or processing what > appears from the start-line to be an HTTP request message, receives a > sequence of octets that does not match the HTTP-message grammar aside from > the robustness exceptions listed above, the server SHOULD respond with a 400 > (Bad Request) response and close the connection. It also says this: > A recipient MUST ignore unrecognized chunk extensions. Certainly pound should be fixed though. I'll let them know. -Ben

