aboutsummaryrefslogtreecommitdiffstats
path: root/test/security_SUITE.erl
diff options
context:
space:
mode:
Diffstat (limited to 'test/security_SUITE.erl')
-rw-r--r--test/security_SUITE.erl81
1 files changed, 78 insertions, 3 deletions
diff --git a/test/security_SUITE.erl b/test/security_SUITE.erl
index f06cec5..944c491 100644
--- a/test/security_SUITE.erl
+++ b/test/security_SUITE.erl
@@ -1,4 +1,4 @@
-%% Copyright (c) 2018, Loïc Hoguin <[email protected]>
+%% Copyright (c) 2018-2024, Loïc Hoguin <[email protected]>
%%
%% Permission to use, copy, modify, and/or distribute this software for any
%% purpose with or without fee is hereby granted, provided that the above
@@ -33,10 +33,12 @@ 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_settings_flood,
@@ -47,10 +49,12 @@ groups() ->
{https, [parallel], Tests ++ H1Tests},
{h2, [parallel], Tests},
{h2c, [parallel], Tests ++ H2CTests},
+ {h3, [], Tests},
{http_compress, [parallel], Tests ++ H1Tests},
{https_compress, [parallel], Tests ++ H1Tests},
{h2_compress, [parallel], Tests},
- {h2c_compress, [parallel], Tests ++ H2CTests}
+ {h2c_compress, [parallel], Tests ++ H2CTests},
+ {h3_compress, [], Tests}
].
init_per_suite(Config) ->
@@ -64,7 +68,7 @@ init_per_group(Name, Config) ->
cowboy_test:init_common_groups(Name, Config, ?MODULE).
end_per_group(Name, _) ->
- cowboy:stop_listener(Name).
+ cowboy_test:stop_group(Name).
%% Routes.
@@ -72,12 +76,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),
@@ -179,6 +222,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. (VU#421644)"),
+ {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)"),