diff options
author | Hamidreza Soleimani <[email protected]> | 2017-10-29 14:33:02 +0100 |
---|---|---|
committer | Hamidreza Soleimani <[email protected]> | 2017-10-29 21:26:42 +0100 |
commit | 70a813c20a829ed47feb6a4b2e7b0332adac6c4f (patch) | |
tree | 1fc201d333059188ad7040707f6d098e2e7c73ae /lib/inets/src/http_client/httpc_response.erl | |
parent | f3d069dd1e3978b240c0f99c5609735e72ea8e8c (diff) | |
download | otp-70a813c20a829ed47feb6a4b2e7b0332adac6c4f.tar.gz otp-70a813c20a829ed47feb6a4b2e7b0332adac6c4f.tar.bz2 otp-70a813c20a829ed47feb6a4b2e7b0332adac6c4f.zip |
[#ERL-407]: Fix httpc misbehaviour based on RFC7230, section 3.3.3
If a message is received with both a Transfer-Encoding and a
Content-Length header field, it might indicate an attempt to
perform request smuggling or response splitting and must be
handled as an error in default mode (not relaxed mode).
Bug report: https://bugs.erlang.org/browse/ERL-407
Diffstat (limited to 'lib/inets/src/http_client/httpc_response.erl')
-rw-r--r-- | lib/inets/src/http_client/httpc_response.erl | 40 |
1 files changed, 37 insertions, 3 deletions
diff --git a/lib/inets/src/http_client/httpc_response.erl b/lib/inets/src/http_client/httpc_response.erl index b3b11b74ab..91638f5d2e 100644 --- a/lib/inets/src/http_client/httpc_response.erl +++ b/lib/inets/src/http_client/httpc_response.erl @@ -269,7 +269,7 @@ parse_headers(<<?LF,?LF,Body/binary>>, Header, Headers, MaxHeaderSize, Result, Relaxed); parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers, - MaxHeaderSize, Result, _) -> + MaxHeaderSize, Result, Relaxed) -> HTTPHeaders = [lists:reverse(Header) | Headers], Length = lists:foldl(fun(H, Acc) -> length(H) + Acc end, 0, HTTPHeaders), @@ -277,8 +277,42 @@ parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers, true -> ResponseHeaderRcord = http_response:headers(HTTPHeaders, #http_response_h{}), - {ok, list_to_tuple( - lists:reverse([Body, ResponseHeaderRcord | Result]))}; + + %% 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. + case ResponseHeaderRcord#http_response_h.'transfer-encoding' of + undefined -> + {ok, list_to_tuple( + lists:reverse([Body, ResponseHeaderRcord | Result]))}; + Value -> + TransferEncoding = string:lowercase(Value), + ContentLength = ResponseHeaderRcord#http_response_h.'content-length', + if + %% Respond without error but remove Content-Length field in relaxed mode + (Relaxed =:= true) + andalso (TransferEncoding =:= "chunked") + andalso (ContentLength =/= "-1") -> + ResponseHeaderRcordFixed = + ResponseHeaderRcord#http_response_h{'content-length' = "-1"}, + {ok, list_to_tuple( + lists:reverse([Body, ResponseHeaderRcordFixed | Result]))}; + %% Respond with error in default (not relaxed) mode + (Relaxed =:= false) + andalso (TransferEncoding =:= "chunked") + andalso (ContentLength =/= "-1") -> + throw({error, {headers_conflict, {'content-length', + 'transfer-encoding'}}}); + true -> + {ok, list_to_tuple( + lists:reverse([Body, ResponseHeaderRcord | Result]))} + end + end; false -> throw({error, {header_too_long, MaxHeaderSize, MaxHeaderSize-Length}}) |