Skip to content

Commit 5bd81ce

Browse files
committed
tls: support TLSv1.3
This introduces TLS1.3 support and makes it the default max protocol, but also supports CLI/NODE_OPTIONS switches to disable it if necessary. TLS1.3 is a major update to the TLS protocol, with many security enhancements. It should be preferred over TLS1.2 whenever possible. TLS1.3 is different enough that even though the OpenSSL APIs are technically API/ABI compatible, that when TLS1.3 is negotiated, the timing of protocol records and of callbacks broke assumptions hard-coded into the 'tls' module. This change introduces no API incompatibilities when TLS1.2 is negotiated. It is the intention that it be backported to current and LTS release lines with the default maximum TLS protocol reset to 'TLSv1.2'. This will allow users of those lines to explicitly enable TLS1.3 if they want. API incompatibilities between TLS1.2 and TLS1.3 are: - Variations of `conn.write('data'); conn.destroy()` have undefined behaviour according to the streams API. They may or may not send the 'data', and may or may not cause a ERR_STREAM_DESTROYED error to be emitted. This has always been true, but conditions under which the write suceeds is slightly but observably different when TLS1.3 is negotiated vs when TLS1.2 or below is negotiated. - If TLS1.3 is negotiated, and a server calls `conn.end()` in its 'secureConnection' listener without any data being written, the client will not receive session tickets (no 'session' events will be emitted, and `conn.getSession()` will never return a resumable session). - The return value of `conn.getSession()` API may not return a resumable session if called right after the handshake. The effect will be that clients using the legacy `getSession()` API will resume sessions if TLS1.2 is negotiated, but will do full handshakes if TLS1.3 is negotiated. See #25831 for more information.
1 parent 3bdd672 commit 5bd81ce

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+1014
-213
lines changed

doc/api/cli.md

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -424,21 +424,43 @@ added: v4.0.0
424424
Specify an alternative default TLS cipher list. Requires Node.js to be built
425425
with crypto support (default).
426426

427-
### `--tls-v1.0`
427+
### `--tls-min-v1.0`
428428
<!-- YAML
429429
added: REPLACEME
430430
-->
431431

432-
Enable TLSv1.0 and greater in default [secureProtocol][]. Use for compatibility
433-
with old TLS clients or servers.
432+
Set default [minVersion][] to 'TLSv1'. Use for compatibility with old TLS
433+
clients or servers.
434434

435-
### `--tls-v1.1`
435+
### `--tls-min-v1.1`
436436
<!-- YAML
437437
added: REPLACEME
438438
-->
439439

440-
Enable TLSv1.1 and greater in default [secureProtocol][]. Use for compatibility
441-
with old TLS clients or servers.
440+
Set default [minVersion][] to 'TLSv1.1'. Use for compatibility with old TLS
441+
clients or servers.
442+
443+
### `--tls-min-v1.3`
444+
<!-- YAML
445+
added: REPLACEME
446+
-->
447+
448+
Set default [minVersion][] to 'TLSv1.3'. Use to disable support for TLSv1.2 in
449+
favour of TLSv1.3, which is more secure.
450+
451+
### `--tls-max-v1.2`
452+
<!-- YAML
453+
added: REPLACEME
454+
-->
455+
456+
Set default [maxVersion][] to 'TLSv1.2'. Use to disable support for TLSv1.3.
457+
458+
### `--tls-max-v1.3`
459+
<!-- YAML
460+
added: REPLACEME
461+
-->
462+
463+
Set default [maxVersion][] to 'TLSv1.3'. Use to enable support for TLSv1.3.
442464

443465
### `--trace-deprecation`
444466
<!-- YAML
@@ -882,4 +904,5 @@ greater than `4` (its current default value). For more information, see the
882904
[experimental ECMAScript Module]: esm.html#esm_loader_hooks
883905
[libuv threadpool documentation]: http://docs.libuv.org/en/latest/threadpool.html
884906
[remote code execution]: https://www.owasp.org/index.php/Code_Injection
885-
[secureProtocol]: tls.html#tls_tls_createsecurecontext_options
907+
[minVersion]: tls.html#tls_tls_createsecurecontext_options
908+
[maxVersion]: tls.html#tls_tls_createsecurecontext_options

doc/api/tls.md

Lines changed: 89 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,9 @@ not required and a default ECDHE curve will be used. The `ecdhCurve` property
104104
can be used when creating a TLS Server to specify the list of names of supported
105105
curves to use, see [`tls.createServer()`] for more info.
106106

107+
Perfect Forward Secrecy was optional up to TLSv1.2, but it is not optional for
108+
TLSv1.3, because all TLSv1.3 cipher suites use ECDHE.
109+
107110
### ALPN and SNI
108111

109112
<!-- type=misc -->
@@ -136,6 +139,8 @@ threshold is exceeded. The limits are configurable:
136139
The default renegotiation limits should not be modified without a full
137140
understanding of the implications and risks.
138141

142+
TLSv1.3 does not support renegotiation.
143+
139144
### Session Resumption
140145

141146
Establishing a TLS session can be relatively slow. The process can be sped
@@ -176,6 +181,10 @@ as for resumption with session tickets. For debugging, if
176181
[`tls.TLSSocket.getTLSTicket()`][] returns a value, the session data contains a
177182
ticket, otherwise it contains client-side session state.
178183

184+
With TLSv1.3, be aware that multiple tickets may be sent by the server,
185+
resulting in multiple `'session'` events, see [`'session'`][] for more
186+
information.
187+
179188
Single process servers need no specific implementation to use session tickets.
180189
To use session tickets across server restarts or load balancers, servers must
181190
all have the same ticket keys. There are three 16-byte keys internally, but the
@@ -230,6 +239,9 @@ Node.js is built with a default suite of enabled and disabled TLS ciphers.
230239
Currently, the default cipher suite is:
231240

232241
```txt
242+
TLS_AES_256_GCM_SHA384:
243+
TLS_CHACHA20_POLY1305_SHA256:
244+
TLS_AES_128_GCM_SHA256:
233245
ECDHE-RSA-AES128-GCM-SHA256:
234246
ECDHE-ECDSA-AES128-GCM-SHA256:
235247
ECDHE-RSA-AES256-GCM-SHA384:
@@ -270,7 +282,19 @@ The default can also be replaced on a per client or server basis using the
270282
in [`tls.createServer()`], [`tls.connect()`], and when creating new
271283
[`tls.TLSSocket`]s.
272284

273-
Consult [OpenSSL cipher list format documentation][] for details on the format.
285+
The ciphers list can contain a mixture of TLSv1.3 cipher suite names, the ones
286+
that start with `'TLS_'`, and specifications for TLSv1.2 and below cipher
287+
suites. The TLSv1.2 ciphers support a legacy specification format, consult
288+
the OpenSSL [cipher list format][] documentation for details, but those
289+
specifications do *not* apply to TLSv1.3 ciphers. The TLSv1.3 suites can only
290+
be enabled by including their full name in the cipher list. They cannot, for
291+
example, be enabled or disabled by using the legacy TLSv1.2 `'EECDH'` or
292+
`'!EECDH'` specification.
293+
294+
Despite the relative order of TLSv1.3 and TLSv1.2 cipher suites, the TLSv1.3
295+
protocol is significantly more secure than TLSv1.2, and will always be chosen
296+
over TLSv1.2 if the handshake indicates it is supported, and if any TLSv1.3
297+
cipher suites are enabled.
274298

275299
The default cipher suite included within Node.js has been carefully
276300
selected to reflect current security best practices and risk mitigation.
@@ -291,6 +315,17 @@ the default configuration. If these clients _must_ be supported, the
291315
[TLS recommendations] may offer a compatible cipher suite. For more details
292316
on the format, see the [OpenSSL cipher list format documentation].
293317

318+
There are only 5 TLSv1.3 cipher suites:
319+
- `'TLS_AES_256_GCM_SHA384'`
320+
- `'TLS_CHACHA20_POLY1305_SHA256'`
321+
- `'TLS_AES_128_GCM_SHA256'`
322+
- `'TLS_AES_128_CCM_SHA256'`
323+
- `'TLS_AES_128_CCM_8_SHA256'`
324+
325+
The first 3 are enabled by default. The last 2 `CCM`-based suites are supported
326+
by TLSv1.3 because they may be more performant on constrained systems, but they
327+
are not enabled by default since they offer less security.
328+
294329
## Class: tls.Server
295330
<!-- YAML
296331
added: v0.3.2
@@ -634,11 +669,11 @@ On the client, the `session` can be provided to the `session` option of
634669

635670
See [Session Resumption][] for more information.
636671

637-
Note: For TLS1.2 and below, [`tls.TLSSocket.getSession()`][] can be called once
638-
the handshake is complete. For TLS1.3, only ticket based resumption is allowed
672+
Note: For TLSv1.2 and below, [`tls.TLSSocket.getSession()`][] can be called once
673+
the handshake is complete. For TLSv1.3, only ticket based resumption is allowed
639674
by the protocol, multiple tickets are sent, and the tickets aren't sent until
640675
later, after the handshake completes, so it is necessary to wait for the
641-
`'session'` event to get a resumable session. Future-proof applications are
676+
`'session'` event to get a resumable session. Applications are
642677
recommended to use the `'session'` event instead of `getSession()` to ensure
643678
they will work for all TLS protocol versions. Applications that only expect to
644679
get or use 1 session should listen for this event only once:
@@ -727,7 +762,7 @@ field which always contains the value `'TLSv1/SSLv3'`.
727762
For example: `{ name: 'AES256-SHA', version: 'TLSv1/SSLv3' }`.
728763

729764
See `SSL_CIPHER_get_name()` in
730-
<https://www.openssl.org/docs/man1.1.0/ssl/SSL_CIPHER_get_name.html> for more
765+
<https://www.openssl.org/docs/man1.1.1/ssl/SSL_CIPHER_get_name.html> for more
731766
information.
732767

733768
### tlsSocket.getEphemeralKeyInfo()
@@ -897,12 +932,13 @@ be returned for server sockets or disconnected client sockets.
897932

898933
Protocol versions are:
899934

935+
* `'SSLv3'`
900936
* `'TLSv1'`
901937
* `'TLSv1.1'`
902938
* `'TLSv1.2'`
903-
* `'SSLv3'`
939+
* `'TLSv1.3'`
904940

905-
See <https://www.openssl.org/docs/man1.1.0/ssl/SSL_get_version.html> for more
941+
See <https://www.openssl.org/docs/man1.1.1/ssl/SSL_get_version.html> for more
906942
information.
907943

908944
### tlsSocket.getSession()
@@ -919,8 +955,8 @@ for debugging.
919955

920956
See [Session Resumption][] for more information.
921957

922-
Note: `getSession()` works only for TLS1.2 and below. Future-proof applications
923-
should use the [`'session'`][] event.
958+
Note: `getSession()` works only for TLSv1.2 and below. For TLSv1.3, applications
959+
must use the [`'session'`][] event (it also works for TLSv1.2 and below).
924960

925961
### tlsSocket.getTLSTicket()
926962
<!-- YAML
@@ -1002,8 +1038,12 @@ added: v0.11.8
10021038
verification fails; `err.code` contains the OpenSSL error code. **Default:**
10031039
`true`.
10041040
* `requestCert`
1005-
* `callback` {Function} A function that will be called when the renegotiation
1006-
request has been completed.
1041+
* `callback` {Function} If `renegotiate()` returned `true`, callback is
1042+
is attached once to the `'secure'` event. If it returned `false`, it will be
1043+
called in the next tick with `ERR_TLS_RENEGOTIATE`, unless the `tlsSocket`
1044+
has been destroyed, in which case it will not be called at all.
1045+
1046+
Returns `true` if renegotiation was initiated, `false` otherwise.
10071047

10081048
The `tlsSocket.renegotiate()` method initiates a TLS renegotiation process.
10091049
Upon completion, the `callback` function will be passed a single argument
@@ -1015,6 +1055,9 @@ connection has been established.
10151055
When running as the server, the socket will be destroyed with an error after
10161056
`handshakeTimeout` timeout.
10171057

1058+
For TLSv1.3, renegotiation cannot be initiated, it is not supported by the
1059+
protocol.
1060+
10181061
### tlsSocket.setMaxSendFragment(size)
10191062
<!-- YAML
10201063
added: v0.11.11
@@ -1213,6 +1256,9 @@ argument.
12131256
<!-- YAML
12141257
added: v0.11.13
12151258
changes:
1259+
- version: REPLACEME
1260+
pr-url: https://github.com/nodejs/node/pull/26209
1261+
description: TLSv1.3 support added.
12161262
- version: v11.5.0
12171263
pr-url: https://github.com/nodejs/node/pull/24733
12181264
description: The `ca:` option now supports `BEGIN TRUSTED CERTIFICATE`.
@@ -1303,15 +1349,22 @@ changes:
13031349
`object.passphrase` is optional. Encrypted keys will be decrypted with
13041350
`object.passphrase` if provided, or `options.passphrase` if it is not.
13051351
* `maxVersion` {string} Optionally set the maximum TLS version to allow. One
1306-
of `TLSv1.2'`, `'TLSv1.1'`, or `'TLSv1'`. Cannot be specified along with the
1307-
`secureProtocol` option, use one or the other. **Default:** `'TLSv1.2'`.
1352+
of `TLSv1.3`, `TLSv1.2'`, `'TLSv1.1'`, or `'TLSv1'`. Cannot be specified
1353+
along with the `secureProtocol` option, use one or the other.
1354+
**Default:** `'TLSv1.3'`, unless changed using CLI options. Using
1355+
`--tls-max-v1.2` sets the default to `'TLSv1.2`'. Using `--tls-max-v1.3`
1356+
sets the default to `'TLSv1.3'`. If multiple of the options are provided,
1357+
the highest maximum is used.
13081358
* `minVersion` {string} Optionally set the minimum TLS version to allow. One
1309-
of `TLSv1.2'`, `'TLSv1.1'`, or `'TLSv1'`. Cannot be specified along with the
1310-
`secureProtocol` option, use one or the other. It is not recommended to use
1311-
less than TLSv1.2, but it may be required for interoperability.
1359+
of `TLSv1.3`, `TLSv1.2'`, `'TLSv1.1'`, or `'TLSv1'`. Cannot be specified
1360+
along with the `secureProtocol` option, use one or the other. It is not
1361+
recommended to use less than TLSv1.2, but it may be required for
1362+
interoperability.
13121363
**Default:** `'TLSv1.2'`, unless changed using CLI options. Using
1313-
`--tls-v1.0` changes the default to `'TLSv1'`. Using `--tls-v1.1` changes
1314-
the default to `'TLSv1.1'`.
1364+
`--tls-min-v1.0` sets the default to `'TLSv1'`. Using `--tls-min-v1.1` sets
1365+
the default to `'TLSv1.1'`. Using `--tls-min-v1.3` sets the default to
1366+
`'TLSv1.3'`. If multiple of the options are provided, the lowest minimum is
1367+
used.
13151368
* `passphrase` {string} Shared passphrase used for a single private key and/or
13161369
a PFX.
13171370
* `pfx` {string|string[]|Buffer|Buffer[]|Object[]} PFX or PKCS12 encoded
@@ -1327,12 +1380,15 @@ changes:
13271380
which is not usually necessary. This should be used carefully if at all!
13281381
Value is a numeric bitmask of the `SSL_OP_*` options from
13291382
[OpenSSL Options][].
1330-
* `secureProtocol` {string} The TLS protocol version to use. The possible
1331-
values are listed as [SSL_METHODS][], use the function names as strings. For
1332-
example, use `'TLSv1_1_method'` to force TLS version 1.1, or `'TLS_method'`
1333-
to allow any TLS protocol version. It is not recommended to use TLS versions
1334-
less than 1.2, but it may be required for interoperability. **Default:**
1335-
none, see `minVersion`.
1383+
* `secureProtocol` {string} Legacy mechanism to select the TLS protocol
1384+
version to use, it does not support independent control of the minimum and
1385+
maximum version, and does not support limiting the protocol to TLSv1.3. Use
1386+
`minVersion` and `maxVersion` instead. The possible values are listed as
1387+
[SSL_METHODS][], use the function names as strings. For example, use
1388+
`'TLSv1_1_method'` to force TLS version 1.1, or `'TLS_method'` to allow any
1389+
TLS protocol version up to TLSv1.3. It is not recommended to use TLS
1390+
versions less than 1.2, but it may be required for interoperability.
1391+
**Default:** none, see `minVersion`.
13361392
* `sessionIdContext` {string} Opaque identifier used by servers to ensure
13371393
session state is not shared between applications. Unused by clients.
13381394

@@ -1450,10 +1506,15 @@ added: v0.10.2
14501506

14511507
* Returns: {string[]}
14521508

1453-
Returns an array with the names of the supported SSL ciphers.
1509+
Returns an array with the names of the supported TLS ciphers. The names are
1510+
lower-case for historical reasons, but must be uppercased to be used in
1511+
the `ciphers` option of [`tls.createSecureContext()`][].
1512+
1513+
Cipher names that start with `'tls_'` are for TLSv1.3, all the others are for
1514+
TLSv1.2 and below.
14541515

14551516
```js
1456-
console.log(tls.getCiphers()); // ['AES128-SHA', 'AES256-SHA', ...]
1517+
console.log(tls.getCiphers()); // ['aes128-gcm-sha256', 'aes128-sha', ...]
14571518
```
14581519

14591520
## tls.DEFAULT_ECDH_CURVE
@@ -1612,16 +1673,16 @@ where `secureSocket` has the same API as `pair.cleartext`.
16121673
[Forward secrecy]: https://en.wikipedia.org/wiki/Perfect_forward_secrecy
16131674
[OCSP request]: https://en.wikipedia.org/wiki/OCSP_stapling
16141675
[OpenSSL Options]: crypto.html#crypto_openssl_options
1615-
[OpenSSL cipher list format documentation]: https://www.openssl.org/docs/man1.1.0/apps/ciphers.html#CIPHER-LIST-FORMAT
16161676
[Perfect Forward Secrecy]: #tls_perfect_forward_secrecy
16171677
[RFC 2246]: https://www.ietf.org/rfc/rfc2246.txt
16181678
[RFC 5077]: https://tools.ietf.org/html/rfc5077
16191679
[RFC 5929]: https://tools.ietf.org/html/rfc5929
1620-
[SSL_METHODS]: https://www.openssl.org/docs/man1.1.0/ssl/ssl.html#Dealing-with-Protocol-Methods
1680+
[SSL_METHODS]: https://www.openssl.org/docs/man1.1.1/ssl/ssl.html#Dealing-with-Protocol-Methods
16211681
[Session Resumption]: #tls_session_resumption
16221682
[Stream]: stream.html#stream_stream
16231683
[TLS recommendations]: https://wiki.mozilla.org/Security/Server_Side_TLS
16241684
[asn1.js]: https://www.npmjs.com/package/asn1.js
16251685
[certificate object]: #tls_certificate_object
1686+
[cipher list format]: https://www.openssl.org/docs/man1.1.1/apps/ciphers.html#CIPHER-LIST-FORMAT
16261687
[modifying the default cipher suite]: #tls_modifying_the_default_tls_cipher_suite
16271688
[specific attacks affecting larger AES key sizes]: https://www.schneier.com/blog/archives/2009/07/another_new_aes.html

doc/node.1

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -233,13 +233,23 @@ Specify process.title on startup.
233233
Specify an alternative default TLS cipher list.
234234
Requires Node.js to be built with crypto support. (Default)
235235
.
236-
.It Fl -tls-v1.0
237-
Enable TLSv1.0 and greater in default secureProtocol. Use for compatibility
238-
with old TLS clients or servers.
236+
.It Fl -tls-min-v1.0
237+
Set default minVersion to 'TLSv1'. Use for compatibility with old TLS clients
238+
or servers.
239239
.
240-
.It Fl -tls-v1.1
241-
Enable TLSv1.1 and greater in default secureProtocol. Use for compatibility
242-
with old TLS clients or servers.
240+
.It Fl -tls-min-v1.1
241+
Set default minVersion to 'TLSv1.1'. Use for compatibility with old TLS clients
242+
or servers.
243+
.
244+
.It Fl -tls-min-v1.3
245+
Set default minVersion to 'TLSv1.3'. Use to disable support for TLSv1.2 in
246+
favour of TLSv1.3, which is more secure.
247+
.
248+
.It Fl -tls-max-v1.2
249+
Set default maxVersion to 'TLSv1.2'. Use to disable support for TLSv1.3.
250+
.
251+
.It Fl -tls-max-v1.3
252+
Set default maxVersion to 'TLSv1.3'. Use to enable support for TLSv1.3.
243253
.
244254
.It Fl -trace-deprecation
245255
Print stack traces for deprecations.

lib/_tls_common.js

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ const {
3535
TLS1_VERSION,
3636
TLS1_1_VERSION,
3737
TLS1_2_VERSION,
38+
TLS1_3_VERSION,
3839
} = internalBinding('constants').crypto;
3940

4041
// Lazily loaded from internal/crypto/util.
@@ -45,6 +46,7 @@ function toV(which, v, def) {
4546
if (v === 'TLSv1') return TLS1_VERSION;
4647
if (v === 'TLSv1.1') return TLS1_1_VERSION;
4748
if (v === 'TLSv1.2') return TLS1_2_VERSION;
49+
if (v === 'TLSv1.3') return TLS1_3_VERSION;
4850
throw new ERR_TLS_INVALID_PROTOCOL_VERSION(v, which);
4951
}
5052

@@ -148,10 +150,31 @@ exports.createSecureContext = function createSecureContext(options) {
148150
}
149151
}
150152

151-
if (options.ciphers)
152-
c.context.setCiphers(options.ciphers);
153-
else
154-
c.context.setCiphers(tls.DEFAULT_CIPHERS);
153+
if (options.ciphers && typeof options.ciphers !== 'string') {
154+
throw new ERR_INVALID_ARG_TYPE(
155+
'options.ciphers', 'string', options.ciphers);
156+
}
157+
158+
// Work around an OpenSSL API quirk. cipherList is for TLSv1.2 and below,
159+
// cipherSuites is for TLSv1.3 (and presumably any later versions). TLSv1.3
160+
// cipher suites all have a standard name format beginning with TLS_, so split
161+
// the ciphers and pass them to the appropriate API.
162+
const ciphers = (options.ciphers || tls.DEFAULT_CIPHERS).split(':');
163+
const cipherList = ciphers.filter((_) => !_.match(/^TLS_/)).join(':');
164+
const cipherSuites = ciphers.filter((_) => _.match(/^TLS_/)).join(':');
165+
166+
// Set cipher suites first, so that setCiphers() will error if there are
167+
// neither TLSv1.2 nor TLSv1.3 cipher suites enabled.
168+
c.context.setCipherSuites(cipherSuites);
169+
c.context.setCiphers(cipherList);
170+
171+
if (cipherSuites === '' && c.context.getMaxProto() > TLS1_2_VERSION &&
172+
c.context.getMinProto() < TLS1_3_VERSION)
173+
c.context.setMaxProto(TLS1_2_VERSION);
174+
175+
if (cipherList === '' && c.context.getMinProto() < TLS1_3_VERSION &&
176+
c.context.getMaxProto() > TLS1_2_VERSION)
177+
c.context.setMinProto(TLS1_3_VERSION);
155178

156179
if (options.ecdhCurve === undefined)
157180
c.context.setECDHCurve(tls.DEFAULT_ECDH_CURVE);

0 commit comments

Comments
 (0)