diff options
author | Loïc Hoguin <[email protected]> | 2024-01-05 16:24:25 +0100 |
---|---|---|
committer | Loïc Hoguin <[email protected]> | 2024-01-05 16:32:59 +0100 |
commit | 6ef79ae410d9bce15a361303ec283f6381965404 (patch) | |
tree | 050db233e9df42e421e96f6df0ff4897dc6d8ec4 /src/cowboy_http.erl | |
parent | 5b2f600036145653c48a7e8a60853e4a0ecc770b (diff) | |
download | cowboy-6ef79ae410d9bce15a361303ec283f6381965404.tar.gz cowboy-6ef79ae410d9bce15a361303ec283f6381965404.tar.bz2 cowboy-6ef79ae410d9bce15a361303ec283f6381965404.zip |
Reject HTTP/1 requests with both content-length and transfer-encoding
The previous behavior was to accept them and drop the
content-length header as per the RFC recommendation.
But since this behavior is not normal it is safer to
just reject such requests than risk security issues.
Diffstat (limited to 'src/cowboy_http.erl')
-rw-r--r-- | src/cowboy_http.erl | 23 |
1 files changed, 13 insertions, 10 deletions
diff --git a/src/cowboy_http.erl b/src/cowboy_http.erl index bba2108..70f8268 100644 --- a/src/cowboy_http.erl +++ b/src/cowboy_http.erl @@ -765,39 +765,42 @@ default_port(_) -> 80. request(Buffer, State0=#state{ref=Ref, transport=Transport, peer=Peer, sock=Sock, cert=Cert, proxy_header=ProxyHeader, in_streamid=StreamID, in_state= PS=#ps_header{method=Method, path=Path, qs=Qs, version=Version}}, - Headers0, Host, Port) -> + Headers, Host, Port) -> Scheme = case Transport:secure() of true -> <<"https">>; false -> <<"http">> end, - {Headers, HasBody, BodyLength, TDecodeFun, TDecodeState} = case Headers0 of + {HasBody, BodyLength, TDecodeFun, TDecodeState} = case Headers of + #{<<"transfer-encoding">> := _, <<"content-length">> := _} -> + error_terminate(400, State0#state{in_state=PS#ps_header{headers=Headers}}, + {stream_error, protocol_error, + 'The request had both transfer-encoding and content-length headers. (RFC7230 3.3.3)'}); #{<<"transfer-encoding">> := TransferEncoding0} -> try cow_http_hd:parse_transfer_encoding(TransferEncoding0) of [<<"chunked">>] -> - {maps:remove(<<"content-length">>, Headers0), - true, undefined, fun cow_http_te:stream_chunked/2, {0, 0}}; + {true, undefined, fun cow_http_te:stream_chunked/2, {0, 0}}; _ -> - error_terminate(400, State0#state{in_state=PS#ps_header{headers=Headers0}}, + error_terminate(400, State0#state{in_state=PS#ps_header{headers=Headers}}, {stream_error, protocol_error, 'Cowboy only supports transfer-encoding: chunked. (RFC7230 3.3.1)'}) catch _:_ -> - error_terminate(400, State0#state{in_state=PS#ps_header{headers=Headers0}}, + error_terminate(400, State0#state{in_state=PS#ps_header{headers=Headers}}, {stream_error, protocol_error, 'The transfer-encoding header is invalid. (RFC7230 3.3.1)'}) end; #{<<"content-length">> := <<"0">>} -> - {Headers0, false, 0, undefined, undefined}; + {false, 0, undefined, undefined}; #{<<"content-length">> := BinLength} -> Length = try cow_http_hd:parse_content_length(BinLength) catch _:_ -> - error_terminate(400, State0#state{in_state=PS#ps_header{headers=Headers0}}, + error_terminate(400, State0#state{in_state=PS#ps_header{headers=Headers}}, {stream_error, protocol_error, 'The content-length header is invalid. (RFC7230 3.3.2)'}) end, - {Headers0, true, Length, fun cow_http_te:stream_identity/2, {0, Length}}; + {true, Length, fun cow_http_te:stream_identity/2, {0, Length}}; _ -> - {Headers0, false, 0, undefined, undefined} + {false, 0, undefined, undefined} end, Req0 = #{ ref => Ref, |