aspnetcore icon indicating copy to clipboard operation
aspnetcore copied to clipboard

Content-Length and Transfer-Encoding conflicts

Open Tratcher opened this issue 2 years ago • 6 comments

Is there an existing issue for this?

  • [X] I have searched the existing issues

Describe the bug

https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2

A sender MUST NOT send a Content-Length header field in any message that contains a Transfer-Encoding header field.

https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.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 9.5) or response splitting (Section 9.4) and ought to be handled as an error. A sender MUST remove the received Content-Length field prior to forwarding such a message downstream.

Kestrel follows this today by honoring the Transfer-Encoding: chunked and ignoring the Content-Length header. However, the Content-Length header is still present on the request and apps may still be consuming it. https://github.com/dotnet/aspnetcore/blob/94cc679bce4967568fd458433ecda3b3fb0578b6/src/Servers/Kestrel/Core/src/Internal/Http/Http1MessageBody.cs#L146-L177

E.g. YARP has to check for this condition too to avoid a smuggling attempt when proxying. https://github.com/microsoft/reverse-proxy/blob/06f28a36140543512a3fc3e1e9423094c43296ec/src/ReverseProxy/Forwarder/HttpTransformer.cs#L80-L92

App developers are not usually aware of spec nuances like this and write logic that makes decisions based on the Content-Length header when present. This can lead to application level functional and security issues like over/under buffering.

Expected Behavior

Proposal: Kestrel should implement the following spec guideline "A sender MUST remove the received Content-Length field prior to forwarding such a message downstream.", treating the application as the downstream entity. If there is a Transfer-Encoding header then kestrel should remove any Content-Length header to avoid app level confusion.

Since Kestrel avoids modifying the request in most cases, giving the app as much of the original data as possible, we want to preserve the Content-Length header value by moving it to x-Content-Length. This should avoid people consuming the unvalidated value by accident, but it's still available for compat reasons.

YARP could remove its mitigation after this change.

Steps To Reproduce

POST / HTTP/1.1
Content-Length: 500000
Transfer-Encoding: chunked

5
Hello
0

Exceptions (if any)

No response

.NET Version

No response

Anything else?

Recommended for .NET 8.

Tratcher avatar Aug 24 '22 17:08 Tratcher

I want to try this, can you assign me?

FatTigerWang avatar Aug 25 '22 08:08 FatTigerWang

Re-opening, we should do the same mitigation for Http.Sys and IIS which today do honor Transfer-Encoding, but continue to flow Content-Length.

Tratcher avatar Sep 15 '22 17:09 Tratcher

@Tratcher Can you help me find the corresponding file? I'm not very familiar with it.

FatTigerWang avatar Sep 16 '22 01:09 FatTigerWang

Here: https://github.com/dotnet/aspnetcore/blob/ce2db7ea0b161fc5eb35710fca6feeafeeac37bc/src/Servers/HttpSys/src/RequestProcessing/Request.cs#L129 It kindof does this already by ignoring the header if Transfer-Encoding is set, but that Transfer-Encoding check shouldn't be an exact match, it should be checking for the last value. https://github.com/dotnet/aspnetcore/blob/ce2db7ea0b161fc5eb35710fca6feeafeeac37bc/src/Servers/HttpSys/src/RequestProcessing/Request.cs#L136-L140 It should also drop the Content-Length header rather than ignoring it.

This should probably be triggered more eagerly from the constructor so apps don't read the CL header directly before it's dropped. https://github.com/dotnet/aspnetcore/blob/main/src/Servers/HttpSys/src/RequestProcessing/Request.cs#L36

Tratcher avatar Sep 20 '22 20:09 Tratcher

If Transfer-Encoding has multiple values, is chunked always at the end? Otherwise I should check all the values.

FatTigerWang avatar Sep 22 '22 04:09 FatTigerWang

chunked is required to be the last entry. Http.Sys enforces this and rejects the request if it's not.

Tratcher avatar Sep 22 '22 15:09 Tratcher

IIS also needs work: https://github.com/dotnet/aspnetcore/blob/01177230911caab93dff106e4f0d973a06f51bf3/src/Servers/IIS/IIS/src/Core/IISHttpContext.cs#L261-L262

That exact match should be checking for the last value instead.

If there's also a Content-Length it should be dropped/renamed.

Tratcher avatar Sep 29 '22 18:09 Tratcher