Skip to content

Server retains Content-Length when Transfer-Encoding is present if Content-Length appears first #4123

Description

@bardiharborow

Version

hyper 1.10.1

Platform

Linux

Summary

In RFC 9112 § 6.1:

Early implementations of Transfer-Encoding would occasionally send both a chunked transfer coding for message framing and an estimated Content-Length header field for use by progress bars. This is why Transfer-Encoding is defined as overriding Content-Length, as opposed to them being mutually incompatible. Unfortunately, forwarding such a message can lead to vulnerabilities regarding request smuggling (Section 11.2) or response splitting (Section 11.1) attacks if any downstream recipient fails to parse the message according to this specification, particularly when a downstream recipient only implements HTTP/1.0.

A server MAY reject a request that contains both Content-Length and Transfer-Encoding or process such a request in accordance with the Transfer-Encoding alone. Regardless, the server MUST close the connection after responding to such a request to avoid the potential attacks.

In RFC 9112 § 6.3:

If a message is received with both a Transfer-Encoding and a Content-Length header field, the Transfer-Encoding overrides the Content-Length. Such a message might indicate an attempt to perform request smuggling (Section 11.2) or response splitting (Section 11.1) and ought to be handled as an error. An intermediary that chooses to forward the message MUST first remove the received Content-Length field and process the Transfer-Encoding (as described below) prior to forwarding the message downstream.

When an HTTP/1.1 message arrives with both the Transfer-Encoding and Content-Length headers, hyper correctly frames the body per the Transfer-Encoding, but the parsed message can still contain the Content-Length header, and whether it does depends on the order the headers appeared on the wire.

In Server::parse, a Content-Length seen after Transfer-Encoding is skipped by the header loop, but a Content-Length seen before it is appended to the header map and survives.

Separately, hyper does not close the connection after responding to such a request, which § 6.1 requires as a MUST.

Code Sample

Expected Behavior

Request:

POST / HTTP/1.1
Host: hyper.rs
Content-Length: 5
Transfer-Encoding: chunked

5
hello
0

Message hyper should hand to the service:

POST / HTTP/1.1
Host: hyper.rs
Transfer-Encoding: chunked

hello

Response hyper should send:

HTTP/1.1 200 OK
Connection: close
Content-Length: 2

ok

Actual Behavior

Message hyper hands to the service:

POST / HTTP/1.1
Host: hyper.rs
Content-Length: 5
Transfer-Encoding: chunked

hello

Response hyper sends:

HTTP/1.1 200 OK
Content-Length: 2

ok

Additional Context

No response

Metadata

Metadata

Assignees

Labels

A-http1Area: HTTP/1 specific.C-bugCategory: bug. Something is wrong. This is bad!E-easyEffort: easy. A task that would be a great starting point for a new contributor.

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions