aboutsummaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
authorViktor Söderqvist <[email protected]>2023-10-31 11:51:02 +0100
committerLoïc Hoguin <[email protected]>2023-12-06 12:41:58 +0100
commit42d87dd7767cde71b7d24633665c0f30ceeb31cf (patch)
tree6ab0084a428f1078237f3233b4d784e1f5a1ac01 /test
parent879a6b8bc5d36e6d91927332ecf9011199844657 (diff)
downloadcowboy-42d87dd7767cde71b7d24633665c0f30ceeb31cf.tar.gz
cowboy-42d87dd7767cde71b7d24633665c0f30ceeb31cf.tar.bz2
cowboy-42d87dd7767cde71b7d24633665c0f30ceeb31cf.zip
Add 'max_cancel_stream_rate' config for the rapid reset attack
Co-authored-by: Björn Svensson <[email protected]>
Diffstat (limited to 'test')
-rw-r--r--test/security_SUITE.erl40
1 files changed, 40 insertions, 0 deletions
diff --git a/test/security_SUITE.erl b/test/security_SUITE.erl
index f06cec5..6e217d5 100644
--- a/test/security_SUITE.erl
+++ b/test/security_SUITE.erl
@@ -39,6 +39,7 @@ groups() ->
http2_empty_frame_flooding_push_promise,
http2_ping_flood,
http2_reset_flood,
+ http2_cancel_flood,
http2_settings_flood,
http2_zero_length_header_leak
],
@@ -72,12 +73,51 @@ init_dispatch(_) ->
cowboy_router:compile([{"localhost", [
{"/", hello_h, []},
{"/echo/:key", echo_h, []},
+ {"/delay_hello", delay_hello_h, 1000},
{"/long_polling", long_polling_h, []},
{"/resp/:key[/:arg]", resp_h, []}
]}]).
%% Tests.
+http2_cancel_flood(Config) ->
+ doc("Confirm that Cowboy detects the rapid reset attack. (CVE-2023-44487)"),
+ do_http2_cancel_flood(Config, 1, 500),
+ do_http2_cancel_flood(Config, 10, 50),
+ do_http2_cancel_flood(Config, 500, 1),
+ ok.
+
+do_http2_cancel_flood(Config, NumStreamsPerBatch, NumBatches) ->
+ {ok, Socket} = rfc7540_SUITE:do_handshake(Config),
+ {HeadersBlock, _} = cow_hpack:encode([
+ {<<":method">>, <<"GET">>},
+ {<<":scheme">>, <<"http">>},
+ {<<":authority">>, <<"localhost">>}, %% @todo Correct port number.
+ {<<":path">>, <<"/delay_hello">>}
+ ]),
+ AllStreamIDs = lists:seq(1, NumBatches * NumStreamsPerBatch * 2, 2),
+ _ = lists:foldl(
+ fun (_BatchNumber, AvailableStreamIDs) ->
+ %% Take a bunch of IDs from the available stream IDs.
+ %% Send HEADERS for all these and then cancel them.
+ {IDs, RemainingStreamIDs} = lists:split(NumStreamsPerBatch, AvailableStreamIDs),
+ _ = gen_tcp:send(Socket, [cow_http2:headers(ID, fin, HeadersBlock) || ID <- IDs]),
+ _ = gen_tcp:send(Socket, [<<4:24, 3:8, 0:8, ID:32, 8:32>> || ID <- IDs]),
+ RemainingStreamIDs
+ end,
+ AllStreamIDs,
+ lists:seq(1, NumBatches, 1)),
+ %% When Cowboy detects a flood it must close the connection.
+ case gen_tcp:recv(Socket, 17, 6000) of
+ {ok, <<_:24, 7:8, 0:8, 0:32, _LastStreamId:32, 11:32>>} ->
+ %% GOAWAY with error code 11 = ENHANCE_YOUR_CALM.
+ ok;
+ %% We also accept the connection being closed immediately,
+ %% which may happen because we send the GOAWAY right before closing.
+ {error, closed} ->
+ ok
+ end.
+
http2_data_dribble(Config) ->
doc("Request a very large response then update the window 1 byte at a time. (CVE-2019-9511)"),
{ok, Socket} = rfc7540_SUITE:do_handshake(Config),