Skip to content

Commit e687c9a

Browse files
committed
ssl: TLS-1.2 session reuse and client certs
Make session_reuse disabled by default when client certificates are requierd, until extend master secret can be implemented. But best option is to run TLS-1.3.
1 parent e850639 commit e687c9a

4 files changed

Lines changed: 53 additions & 11 deletions

File tree

lib/ssl/doc/guides/ssl_hardening.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,35 @@ avoid possible [DoS-attacks](https://en.wikipedia.org/wiki/Denial-of-service_att
174174
By default, the server mitigates renegotiation abuse by enforcing a
175175
12-second delay between client initiated renegotiations.
176176

177+
### TLS 1.2 Session Resumption and Client Certificates
178+
179+
> #### Warning {: .warning }
180+
> TLS 1.2 session resumption (session ID and RFC 5077 tickets) does
181+
> not bind the master secret to the handshake transcript. When client
182+
> certificate authentication is required, this leaves the connection
183+
> vulnerable to the Triple Handshake attack (see RFC 7627). This
184+
> concern does not apply to TLS 1.3, which binds all session keys to
185+
> the full transcript.
186+
187+
If your TLS 1.2 server uses {verify, verify_peer} to require client
188+
certificates make sure session resumption is disabled:
189+
190+
```erlang
191+
{reuse_sessions, false}
192+
```
193+
This eliminates the Triple Handshake attack surface at the cost of a
194+
full handshake for every connection. For deployments that need both
195+
mutual authentication and session resumption over TLS 1.2, upgrading
196+
to TLS 1.3 is the recommended solution — TLS 1.3 session tickets are
197+
inherently safe.
198+
199+
Note that this concern only applies to the server role. A client
200+
setting `verify_peer` to verify the server is not affected.
201+
202+
The default value for `reuse_sessions` in above described configuration
203+
is false since OTP @OTP-20289@
204+
205+
177206
### Key Exchange Groups
178207
TLS-1.3 decouples key exchange algorithms from cipher suites. The key
179208
exchange algorithms are configured using the

lib/ssl/src/ssl_config.erl

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1388,7 +1388,16 @@ opt_reuse_sessions(UserOpts, #{versions := Versions} = Opts, #{role := client})
13881388
assert_version_dep(Where2 =:= new, reuse_session, Versions, ['tlsv1','tlsv1.1','tlsv1.2']),
13891389
Opts#{reuse_sessions => RUSS, reuse_session => RS};
13901390
opt_reuse_sessions(UserOpts, #{versions := Versions} = Opts, #{role := server}) ->
1391-
{Where1, RUSS} = get_opt_bool(reuse_sessions, true, UserOpts, Opts),
1391+
%% Disable reuse_sessions when client cert verify_peer
1392+
%% verification is required, until Extended Master Secret is implemented.
1393+
ReuseDefault = case maps:get(verify, Opts) of
1394+
verify_peer ->
1395+
false;
1396+
verify_none ->
1397+
true
1398+
end,
1399+
1400+
{Where1, RUSS} = get_opt_bool(reuse_sessions, ReuseDefault, UserOpts, Opts),
13921401

13931402
DefRS = fun(_, _, _, _) -> true end,
13941403
{Where2, RS} = get_opt_fun(reuse_session, 4, DefRS, UserOpts, Opts),

lib/ssl/test/ssl_session_SUITE.erl

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ reuse_session_expired(Config) when is_list(Config) ->
206206
{from, self()},
207207
{mfa, {ssl_test_lib, no_result, []}},
208208
{tcp_options, [{active, false}]},
209-
{options, ServerOpts}]),
209+
{options, [{reuse_sessions, true}| ServerOpts]}]),
210210
Port0 = ssl_test_lib:inet_port(Server0),
211211

212212
Client0 = ssl_test_lib:start_client([{node, ClientNode},
@@ -338,7 +338,7 @@ explicit_session_reuse(Config) when is_list(Config) ->
338338
ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
339339
{from, self()},
340340
{mfa, {ssl_test_lib, no_result, []}},
341-
{options, ServerOpts}]),
341+
{options, [{reuse_sessions, true} | ServerOpts]}]),
342342
Port = ssl_test_lib:inet_port(Server),
343343
{Client0, Client0Sock} =
344344
ssl_test_lib:start_client([{node, ClientNode},
@@ -491,7 +491,8 @@ no_reuses_session_server_restart_new_cert() ->
491491
[{doc,"Check that a session is not reused if the server is restarted with a new cert."}].
492492
no_reuses_session_server_restart_new_cert(Config) when is_list(Config) ->
493493
ClientOpts = ssl_test_lib:ssl_options(client_rsa_der_opts, Config),
494-
ServerOpts = ssl_test_lib:ssl_options(server_rsa_der_verify_opts, Config),
494+
ServerOpts = [{reuse_sessions, true} |
495+
ssl_test_lib:ssl_options(server_rsa_der_verify_opts, Config)],
495496
POpts = proplists:get_value(group_opts, Config, []),
496497

497498
#{client_config := NewCOpts,
@@ -504,16 +505,17 @@ no_reuses_session_server_restart_new_cert(Config) when is_list(Config) ->
504505
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
505506

506507
Server0 =
507-
ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
508-
{from, self()},
508+
ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
509+
{from, self()},
509510
{mfa, {ssl_test_lib, session_info_result, []}},
510-
{options, ServerOpts}]),
511+
{options, ServerOpts}]),
511512
Port = ssl_test_lib:inet_port(Server0),
512513
Client0 =
513514
ssl_test_lib:start_client([{node, ClientNode},
514515
{port, Port}, {host, Hostname},
515516
{mfa, {ssl_test_lib, session_info_result, []}},
516-
{from, self()}, {options, [{reuse_sessions, save} | ClientOpts]}]),
517+
{from, self()},
518+
{options, [{reuse_sessions, save} | ClientOpts]}]),
517519
Info0 = receive {Server0, Info00} -> Info00 end,
518520
Info0 = receive {Client0, Info01} -> Info01 end,
519521

@@ -620,7 +622,8 @@ client_max_session_table(Config) when is_list(Config)->
620622
ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
621623
ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
622624
{ClientNode, ServerNode, HostName} = ssl_test_lib:run_where(Config),
623-
test_max_session_limit(ClientOpts,ServerOpts,ClientNode, ServerNode, HostName),
625+
test_max_session_limit(ClientOpts,[{reuse_sessions, true} | ServerOpts],
626+
ClientNode, ServerNode, HostName),
624627
%% Explicit check table size
625628
{status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)),
626629
[_, _,_, _, Prop] = StatusInfo,
@@ -635,7 +638,8 @@ server_max_session_table(Config) when is_list(Config)->
635638
ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config),
636639
ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config),
637640
{ClientNode, ServerNode, HostName} = ssl_test_lib:run_where(Config),
638-
test_max_session_limit(ClientOpts,ServerOpts,ClientNode, ServerNode, HostName),
641+
test_max_session_limit(ClientOpts,[{reuse_sessions, true} | ServerOpts],
642+
ClientNode, ServerNode, HostName),
639643
%% Explicit check table size
640644
SupName = sup_name(ServerOpts),
641645
Sup = whereis(SupName),

lib/ssl/test/ssl_test_lib.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4100,7 +4100,7 @@ reuse_session(ClientOpts, ServerOpts, Config) ->
41004100
{from, self()},
41014101
{mfa, {ssl_test_lib, no_result, []}},
41024102
{tcp_options, [{active, false}]},
4103-
{options, ServerOpts}]),
4103+
{options, [{reuse_sessions, true} | ServerOpts]}]),
41044104
Port0 = inet_port(Server0),
41054105

41064106
Client0 = start_client([{node, ClientNode},

0 commit comments

Comments
 (0)