aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2024-03-14 12:36:54 +0100
committerLoïc Hoguin <[email protected]>2024-03-14 12:56:33 +0100
commitcf71c742d6e04b625be1f32217c9ed11a1bd32b6 (patch)
treeb1dc5628e2deb7ec121c03ee7eb765713fb60e6b
parent81f3a21474155f68fbf494b7026b9678027d303e (diff)
downloadcowboy-cf71c742d6e04b625be1f32217c9ed11a1bd32b6.tar.gz
cowboy-cf71c742d6e04b625be1f32217c9ed11a1bd32b6.tar.bz2
cowboy-cf71c742d6e04b625be1f32217c9ed11a1bd32b6.zip
Add max_fragmented_header_block_size HTTP/2 option
-rw-r--r--Makefile2
-rw-r--r--doc/src/manual/cowboy_http2.asciidoc9
-rw-r--r--rebar.config2
-rw-r--r--src/cowboy_http2.erl1
-rw-r--r--test/security_SUITE.erl35
5 files changed, 46 insertions, 3 deletions
diff --git a/Makefile b/Makefile
index 1be1885..03c4f28 100644
--- a/Makefile
+++ b/Makefile
@@ -15,7 +15,7 @@ CT_OPTS += -ct_hooks cowboy_ct_hook [] # -boot start_sasl
LOCAL_DEPS = crypto
DEPS = cowlib ranch
-dep_cowlib = git https://github.com/ninenines/cowlib 2.12.1
+dep_cowlib = git https://github.com/ninenines/cowlib 2.13.0
dep_ranch = git https://github.com/ninenines/ranch 1.8.0
DOC_DEPS = asciideck
diff --git a/doc/src/manual/cowboy_http2.asciidoc b/doc/src/manual/cowboy_http2.asciidoc
index 8eb3cf2..1d2619c 100644
--- a/doc/src/manual/cowboy_http2.asciidoc
+++ b/doc/src/manual/cowboy_http2.asciidoc
@@ -35,6 +35,7 @@ opts() :: #{
max_connection_window_size => 0..16#7fffffff,
max_decode_table_size => non_neg_integer(),
max_encode_table_size => non_neg_integer(),
+ max_fragmented_header_block_size => 16384..16#7fffffff,
max_frame_size_received => 16384..16777215,
max_frame_size_sent => 16384..16777215 | infinity,
max_received_frame_rate => {pos_integer(), timeout()},
@@ -172,6 +173,14 @@ Maximum header table size in bytes used by the encoder. The server will
compare this value to what the client advertises and choose the smallest
one as the encoder's header table size.
+max_fragmented_header_block_size (32768)::
+
+Maximum header block size when headers are split over multiple HEADERS
+and CONTINUATION frames. Clients that attempt to send header blocks
+larger than this value will receive an ENHANCE_YOUR_CALM connection
+error. Note that this value is not advertised and should be large
+enough for legitimate requests.
+
max_frame_size_received (16384)::
Maximum size in bytes of the frames received by the server. This value is
diff --git a/rebar.config b/rebar.config
index 08bb1ec..27d0696 100644
--- a/rebar.config
+++ b/rebar.config
@@ -1,4 +1,4 @@
{deps, [
-{cowlib,".*",{git,"https://github.com/ninenines/cowlib","2.12.1"}},{ranch,".*",{git,"https://github.com/ninenines/ranch","1.8.0"}}
+{cowlib,".*",{git,"https://github.com/ninenines/cowlib","2.13.0"}},{ranch,".*",{git,"https://github.com/ninenines/ranch","1.8.0"}}
]}.
{erl_opts, [debug_info,warn_export_vars,warn_shadow_vars,warn_obsolete_guard,warn_missing_spec,warn_untyped_record]}.
diff --git a/src/cowboy_http2.erl b/src/cowboy_http2.erl
index 91e9c51..5b1f1e1 100644
--- a/src/cowboy_http2.erl
+++ b/src/cowboy_http2.erl
@@ -44,6 +44,7 @@
max_connection_window_size => 0..16#7fffffff,
max_decode_table_size => non_neg_integer(),
max_encode_table_size => non_neg_integer(),
+ max_fragmented_header_block_size => 16384..16#7fffffff,
max_frame_size_received => 16384..16777215,
max_frame_size_sent => 16384..16777215 | infinity,
max_received_frame_rate => {pos_integer(), timeout()},
diff --git a/test/security_SUITE.erl b/test/security_SUITE.erl
index fb63007..a1ba916 100644
--- a/test/security_SUITE.erl
+++ b/test/security_SUITE.erl
@@ -33,13 +33,14 @@ groups() ->
Tests = [nc_rand, nc_zero],
H1Tests = [slowloris, slowloris_chunks],
H2CTests = [
+ http2_cancel_flood,
http2_data_dribble,
http2_empty_frame_flooding_data,
http2_empty_frame_flooding_headers_continuation,
http2_empty_frame_flooding_push_promise,
+ http2_infinite_continuations,
http2_ping_flood,
http2_reset_flood,
- http2_cancel_flood,
http2_settings_flood,
http2_zero_length_header_leak
],
@@ -219,6 +220,38 @@ http2_empty_frame_flooding_push_promise(Config) ->
{ok, <<_:24, 7:8, _:72, 1:32>>} = gen_tcp:recv(Socket, 17, 6000),
ok.
+http2_infinite_continuations(Config) ->
+ doc("Confirm that Cowboy rejects CONTINUATION frames when the "
+ "total size of HEADERS + CONTINUATION(s) exceeds the limit."),
+ {ok, Socket} = rfc7540_SUITE:do_handshake(Config),
+ %% Send a HEADERS frame followed by a large number
+ %% of continuation frames.
+ {HeadersBlock, _} = cow_hpack:encode([
+ {<<":method">>, <<"GET">>},
+ {<<":scheme">>, <<"http">>},
+ {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
+ {<<":path">>, <<"/">>}
+ ]),
+ HeadersBlockLen = iolist_size(HeadersBlock),
+ ok = gen_tcp:send(Socket, [
+ %% HEADERS frame.
+ <<
+ HeadersBlockLen:24, 1:8, 0:5,
+ 0:1, %% END_HEADERS
+ 0:1,
+ 1:1, %% END_STREAM
+ 0:1,
+ 1:31 %% Stream ID.
+ >>,
+ HeadersBlock,
+ %% CONTINUATION frames.
+ [<<1024:24, 9:8, 0:8, 0:1, 1:31, 0:1024/unit:8>>
+ || _ <- lists:seq(1, 100)]
+ ]),
+ %% Receive an ENHANCE_YOUR_CALM connection error.
+ {ok, <<_:24, 7:8, _:72, 11:32>>} = gen_tcp:recv(Socket, 17, 6000),
+ ok.
+
%% @todo http2_internal_data_buffering(Config) -> I do not know how to test this.
% doc("Request many very large responses, with a larger than necessary window size, "
% "but do not attempt to read from the socket. (CVE-2019-9517)"),