Subject: 204 no content response with Content-Length header.

From: "Adrien de Croy" <[email-masked]>
Date: Thu, 28 May 2015 11:48:03 +0000

Hi everyone.

I'm seeing some responses coming back from a server with status code 204 
and a non-zero Content-Length header.

Since the spec says that 204 responses MUST not contain a body, and 
therefore are terminated by the first empty line, what should one make 
of the Content-Length header?

Should we (in a proxy)

a) ignore it and pass it to client
b) strip it from message to client
c) treat it as some meta data being communicated / updated
d) something else

Regards

Adrien de Croy

---

From: Willy Tarreau <[email-masked]>
Date: Thu, 28 May 2015 13:58:06 +0200

Hi Adrien,

> c) treat it as some meta data being communicated / updated

In haproxy, we process them exactly like 304 which are defined pretty
similarly, in that we ignore the content-length since the message doesn't
contain one. This is the behaviour defined in 7230#3.3.3 :

   1.  Any response to a HEAD request and any response with a 1xx
       (Informational), 204 (No Content), or 304 (Not Modified) status
       code is always terminated by the first empty line after the
       header fields, regardless of the header fields present in the
       message, and thus cannot contain a message body.

Please note the "regardless of the header fields present in the message",
this is the key here.

Best regards,
Willy

---

From: "Adrien de Croy" <[email-masked]>
Date: Thu, 28 May 2015 12:03:47 +0000

ok thanks

I guess the issue is that even if you ignore the Content-Length in the 
message, the framing on the stream is potentially broken, since if the 
emitter of the message decides to send a body of that length, you can't 
process it as another message.

Is this a potential smuggling attack of some sort?

Also whilst a 304 can update metadata and the Content-Length can be used 
to validate a stored entity, it's not clear 204 does.

Adrien

---

From: "Adrien de Croy" <[email-masked]>
Date: Thu, 28 May 2015 12:30:25 +0000

Actually I checked our code and we do ignore it, but do pass it on.

This appears to be breaking some clients (e.g. some web filtering AV 
client which doesn't know to ignore it).

So we might want to revisit the approach of passing it on.

Adrien

---

From: Willy Tarreau <[email-masked]>
Date: Thu, 28 May 2015 14:41:15 +0200

> process it as another message.

But it must not send one, that's explcitly forbidden by the spec. It's
as broken as sending a body in response to a HEAD request.

> Is this a potential smuggling attack of some sort?

If it does so, absolutely since it will desynchronize the reader,
thinking it's reading the response to the second request while
reading the body of the first one!

> to validate a stored entity, it's not clear 204 does.

I agree. The other possibility is to break the connection as the response
violates the spec.

An intermediate solution could consist in only sending the headers to the
client and breaking the connection after that. Any possibly pipelined
request will be replayed without issue since pipeline is only permitted
for idempotent requests.

Willy

---

From: Greg Wilkins <[email-masked]>
Date: Fri, 29 May 2015 12:40:34 +1000

Surely that connection is terminal and must be closed.   It is impossible
for the proxy to determine if the origin server has erred by adding a
content-length when there is no body or by adding a body to a 204 when it
should not have.   If it ignores the body indicated by the content-length
then it will be vulnerable to a smuggling attack.

At the very least, at a Connection:close.

But you'd be in your rights to just send a 500 and close.

cheers

-- 
Greg Wilkins <[email-masked] <[email-masked]>>  - *an Intalio.com
subsidiary*
http://eclipse.org/jetty HTTP, SPDY, Websocket server and client that scales
http://www.webtide.com  advice and support for jetty and cometd.

---

From: Willy Tarreau <[email-masked]>
Date: Fri, 29 May 2015 08:03:51 +0200

> then it will be vulnerable to a smuggling attack.

Not necessarily if everyone in the chain acts as specified both in 2616
and 7230 : response message doesn't contain a body *regardless of headers*.

Cheers,
Willy

---

From: Greg Wilkins <[email-masked]>
Date: Fri, 29 May 2015 16:26:22 +1000

> and 7230 : response message doesn't contain a body *regardless of headers*.

except by definition this situation occurs when somebody in the chain is
acting NOT according to 2616/7230 and has either attached a body when they
should not have, or indicated a content-length when there is no content.

My point is that it is dangerous to try to distinguish between those two
alternatives. Somebody in the chain is acting badly, so cut the chain!

cheers

---

From: Willy Tarreau <[email-masked]>
Date: Fri, 29 May 2015 08:55:15 +0200

>From a spec interpretation perspective I disagree since if you look at
the paragraph about message size, it's pretty clear that content-length
doesn't matter for 204. *BUT* from a developer point of view, I cannot
see a valid case where a 204 could contain a non-zero content-length,
so I'd be tempted to explain that situation via a bug only, and then I
agree with the principle of cutting the chain to prevent that bug from
having nastier effects.

The case where you'd find a transfer-encoding header could be slightly
more difficult to sort out since you're supposed to ignore it with 204
though you don't know yet if you'll find some data, and a bogus server
could unconditionally respond with 204+T-E and a zero-length chunk to
mark the end of the body. In that case there's no risk of smuggling
attack since the body will always start with hex digits, so there's no
way they'll match "HTTP/1.1 <status>" and be confused with a second
response.

Cheers,
Willy

---

From: Amos Jeffries <[email-masked]>
Date: Fri, 29 May 2015 21:29:53 +1200

On 29/05/2015 6:55 p.m., Willy Tarreau wrote:
>> On 29 May 2015 at 16:03, Willy Tarreau wrote:
>>

Except for encodings which are not "chunked". With nothing to transfer
T-E should not exist either by the definition of 204.

Smuggling attack or not closing the connection on garbage is the right
way to go. That passes the harm back to the broken server in the form of
extra TCP round trips each time it does it.

Amos