Replay attack mitigation

Storage of recently used 1st packets added. Connections with the same
1st packet are disallowed
This commit is contained in:
Сергей Прохоров 2019-05-20 03:14:32 +02:00
parent 07d397ce93
commit 0f4d180a06
No known key found for this signature in database
GPG key ID: 1C570244E4EF3337
6 changed files with 304 additions and 4 deletions

View file

@ -2,6 +2,7 @@
-module(mtp_test_client).
-export([connect/5,
connect/6,
send/2,
recv_packet/2,
recv_all/2,
@ -16,13 +17,17 @@
-type tcp_error() :: inet:posix() | closed. % | timeout.
connect(Host, Port, Secret, DcId, Protocol) ->
Seed = crypto:strong_rand_bytes(58),
connect(Host, Port, Seed, Secret, DcId, Protocol).
connect(Host, Port, Seed, Secret, DcId, Protocol) ->
Opts = [{packet, raw},
{mode, binary},
{active, false},
{buffer, 1024},
{send_timeout, 5000}],
{ok, Sock} = gen_tcp:connect(Host, Port, Opts, 1000),
{Header, _, _, CryptoLayer} = mtp_obfuscated:client_create(Secret, Protocol, DcId),
{Header, _, _, CryptoLayer} = mtp_obfuscated:client_create(Seed, Secret, Protocol, DcId),
ok = gen_tcp:send(Sock, Header),
PacketLayer = Protocol:new(),
Codec = mtp_codec:new(mtp_obfuscated, CryptoLayer,

View file

@ -12,7 +12,8 @@
packet_too_large_case/1,
downstream_size_backpressure_case/1,
downstream_qlen_backpressure_case/1,
config_change_case/1
config_change_case/1,
replay_attack_case/1
]).
-export([set_env/2,
@ -308,6 +309,36 @@ config_change_case(Cfg) when is_list(Cfg) ->
?assertEqual(PortsBefore, mtproto_proxy_app:running_ports()),
ok.
%% @doc test replay attack protection.
%% Attempts to connect with the same 1st 64-byte packet should be rejected.
replay_attack_case({pre, Cfg}) ->
setup_single(?FUNCTION_NAME, 10000 + ?LINE, #{}, Cfg);
replay_attack_case({post, Cfg}) ->
stop_single(Cfg);
replay_attack_case(Cfg) when is_list(Cfg) ->
DcId = ?config(dc_id, Cfg),
Host = ?config(mtp_host, Cfg),
Port = ?config(mtp_port, Cfg),
Secret = ?config(mtp_secret, Cfg),
Seed = crypto:strong_rand_bytes(58),
ErrCount = fun() ->
mtp_test_metric:get_tags(
count, [?APP, protocol_error, total], [replay_session_detected])
end,
?assertEqual(not_found, ErrCount()),
Cli1 = mtp_test_client:connect(Host, Port, Seed, Secret, DcId, mtp_secure),
_Cli1_1 = mtp_test_client:send(crypto:strong_rand_bytes(64), Cli1),
?assertEqual(not_found, ErrCount()),
Cli2 = mtp_test_client:connect(Host, Port, Seed, Secret, DcId, mtp_secure),
?assertEqual(
ok, mtp_test_metric:wait_for_value(
count, [?APP, protocol_error, total], [replay_session_detected], 1, 5000),
{mtp_session_storage:status(),
sys:get_state(mtp_test_metric)}),
?assertEqual(1, ErrCount()),
?assertEqual({error, closed}, mtp_test_client:recv_packet(Cli2, 1000)).
%% TODO: send a lot, not read, and then close - assert connection IDs are cleaned up
%% Helpers