Skip to content

Commit b76931b

Browse files
sam-githubMylesBorins
authored andcommitted
doc: describe TLS session resumption
PR-URL: #25174 Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Vse Mozhet Byt <[email protected]>
1 parent c84b4fb commit b76931b

File tree

1 file changed

+147
-39
lines changed

1 file changed

+147
-39
lines changed

doc/api/tls.md

+147-39
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,94 @@ To test the renegotiation limits on a server, connect to it using the OpenSSL
140140
command-line client (`openssl s_client -connect address:port`) then input
141141
`R<CR>` (i.e., the letter `R` followed by a carriage return) multiple times.
142142

143+
### Session Resumption
144+
145+
Establishing a TLS session can be relatively slow. The process can be sped
146+
up by saving and later reusing the session state. There are several mechanisms
147+
to do so, discussed here from oldest to newest (and preferred).
148+
149+
***Session Identifiers*** Servers generate a unique ID for new connections and
150+
send it to the client. Clients and servers save the session state. When
151+
reconnecting, clients send the ID of their saved session state and if the server
152+
also has the state for that ID, it can agree to use it. Otherwise, the server
153+
will create a new session. See [RFC 2246][] for more information, page 23 and
154+
30.
155+
156+
Resumption using session identifiers is supported by most web browsers when
157+
making HTTPS requests.
158+
159+
For Node.js, clients must call [`tls.TLSSocket.getSession()`][] after the
160+
[`'secureConnect'`][] event to get the session data, and provide the data to the
161+
`session` option of [`tls.connect()`][] to reuse the session. Servers must
162+
implement handlers for the [`'newSession'`][] and [`'resumeSession'`][] events
163+
to save and restore the session data using the session ID as the lookup key to
164+
reuse sessions. To reuse sessions across load balancers or cluster workers,
165+
servers must use a shared session cache (such as Redis) in their session
166+
handlers.
167+
168+
***Session Tickets*** The servers encrypt the entire session state and send it
169+
to the client as a "ticket". When reconnecting, the state is sent to the server
170+
in the initial connection. This mechanism avoids the need for server-side
171+
session cache. If the server doesn't use the ticket, for any reason (failure
172+
to decrypt it, it's too old, etc.), it will create a new session and send a new
173+
ticket. See [RFC 5077][] for more information.
174+
175+
Resumption using session tickets is becoming commonly supported by many web
176+
browsers when making HTTPS requests.
177+
178+
For Node.js, clients use the same APIs for resumption with session identifiers
179+
as for resumption with session tickets. For debugging, if
180+
[`tls.TLSSocket.getTLSTicket()`][] returns a value, the session data contains a
181+
ticket, otherwise it contains client-side session state.
182+
183+
Single process servers need no specific implementation to use session tickets.
184+
To use session tickets across server restarts or load balancers, servers must
185+
all have the same ticket keys. There are three 16-byte keys internally, but the
186+
tls API exposes them as a single 48-byte buffer for convenience.
187+
188+
Its possible to get the ticket keys by calling [`server.getTicketKeys()`][] on
189+
one server instance and then distribute them, but it is more reasonable to
190+
securely generate 48 bytes of secure random data and set them with the
191+
`ticketKeys` option of [`tls.createServer()`][]. The keys should be regularly
192+
regenerated and server's keys can be reset with
193+
[`server.setTicketKeys()`][].
194+
195+
Session ticket keys are cryptographic keys, and they ***must be stored
196+
securely***. With TLS 1.2 and below, if they are compromised all sessions that
197+
used tickets encrypted with them can be decrypted. They should not be stored
198+
on disk, and they should be regenerated regularly.
199+
200+
If clients advertise support for tickets, the server will send them. The
201+
server can disable tickets by supplying
202+
`require('constants').SSL_OP_NO_TICKET` in `secureOptions`.
203+
204+
Both session identifiers and session tickets timeout, causing the server to
205+
create new sessions. The timeout can be configured with the `sessionTimeout`
206+
option of [`tls.createServer()`][].
207+
208+
For all the mechanisms, when resumption fails, servers will create new sessions.
209+
Since failing to resume the session does not cause TLS/HTTPS connection
210+
failures, it is easy to not notice unnecessarily poor TLS performance. The
211+
OpenSSL CLI can be used to verify that servers are resuming sessions. Use the
212+
`-reconnect` option to `openssl s_client`, for example:
213+
214+
```sh
215+
$ openssl s_client -connect localhost:443 -reconnect
216+
```
217+
218+
Read through the debug output. The first connection should say "New", for
219+
example:
220+
221+
```text
222+
New, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
223+
```
224+
225+
Subsequent connections should say "Reused", for example:
226+
227+
```text
228+
Reused, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
229+
```
230+
143231
## Modifying the Default TLS Cipher suite
144232

145233
Node.js is built with a default suite of enabled and disabled TLS ciphers.
@@ -169,10 +257,10 @@ HIGH:
169257
!CAMELLIA
170258
```
171259

172-
This default can be replaced entirely using the [`--tls-cipher-list`][] command line
173-
switch (directly, or via the [`NODE_OPTIONS`][] environment variable). For
174-
instance, the following makes `ECDHE-RSA-AES128-GCM-SHA256:!RC4` the default
175-
TLS cipher suite:
260+
This default can be replaced entirely using the [`--tls-cipher-list`][] command
261+
line switch (directly, or via the [`NODE_OPTIONS`][] environment variable). For
262+
instance, the following makes `ECDHE-RSA-AES128-GCM-SHA256:!RC4` the default TLS
263+
cipher suite:
176264

177265
```sh
178266
node --tls-cipher-list="ECDHE-RSA-AES128-GCM-SHA256:!RC4" server.js
@@ -221,11 +309,13 @@ added: v0.9.2
221309
-->
222310

223311
The `'newSession'` event is emitted upon creation of a new TLS session. This may
224-
be used to store sessions in external storage. The listener callback is passed
225-
three arguments when called:
312+
be used to store sessions in external storage. The data should be provided to
313+
the [`'resumeSession'`][] callback.
314+
315+
The listener callback is passed three arguments when called:
226316

227-
* `sessionId` - The TLS session identifier
228-
* `sessionData` - The TLS session data
317+
* `sessionId` {Buffer} The TLS session identifier
318+
* `sessionData` {Buffer} The TLS session data
229319
* `callback` {Function} A callback function taking no arguments that must be
230320
invoked in order for data to be sent or received over the secure connection.
231321

@@ -288,15 +378,19 @@ The `'resumeSession'` event is emitted when the client requests to resume a
288378
previous TLS session. The listener callback is passed two arguments when
289379
called:
290380

291-
* `sessionId` - The TLS/SSL session identifier
381+
* `sessionId` {Buffer} The TLS session identifier
292382
* `callback` {Function} A callback function to be called when the prior session
293-
has been recovered.
294-
295-
When called, the event listener may perform a lookup in external storage using
296-
the given `sessionId` and invoke `callback(null, sessionData)` once finished. If
297-
the session cannot be resumed (i.e., doesn't exist in storage) the callback may
298-
be invoked as `callback(null, null)`. Calling `callback(err)` will terminate the
299-
incoming connection and destroy the socket.
383+
has been recovered: `callback([err[, sessionData]])`
384+
* `err` {Error}
385+
* `sessionData` {Buffer}
386+
387+
The event listener should perform a lookup in external storage for the
388+
`sessionData` saved by the [`'newSession'`][] event handler using the given
389+
`sessionId`. If found, call `callback(null, sessionData)` to resume the session.
390+
If not found, the session cannot be resumed. `callback()` must be called
391+
without `sessionData` so that the handshake can continue and a new session can
392+
be created. It is possible to call `callback(err)` to terminate the incoming
393+
connection and destroy the socket.
300394

301395
Listening for this event will have an effect only on connections established
302396
after the addition of the event listener.
@@ -406,10 +500,11 @@ Returns the current number of concurrent connections on the server.
406500
added: v3.0.0
407501
-->
408502

409-
* Returns: {Buffer}
503+
* Returns: {Buffer} A 48-byte buffer containing the session ticket keys.
410504

411-
Returns a `Buffer` instance holding the keys currently used for
412-
encryption/decryption of the [TLS Session Tickets][].
505+
Returns the session ticket keys.
506+
507+
See [Session Resumption][] for more information.
413508

414509
### server.listen()
415510

@@ -421,17 +516,15 @@ This method is identical to [`server.listen()`][] from [`net.Server`][].
421516
added: v3.0.0
422517
-->
423518

424-
* `keys` {Buffer} The keys used for encryption/decryption of the
425-
[TLS Session Tickets][].
426-
427-
Updates the keys for encryption/decryption of the [TLS Session Tickets][].
519+
* `keys` {Buffer} A 48-byte buffer containing the session ticket keys.
428520

429-
The key's `Buffer` should be 48 bytes long. See `ticketKeys` option in
430-
[`tls.createServer()`] for more information on how it is used.
521+
Sets the session ticket keys.
431522

432523
Changes to the ticket keys are effective only for future server connections.
433524
Existing or currently pending server connections will use the previous keys.
434525

526+
See [Session Resumption][] for more information.
527+
435528
## Class: tls.TLSSocket
436529
<!-- YAML
437530
added: v0.11.4
@@ -695,19 +788,28 @@ information.
695788
added: v0.11.4
696789
-->
697790

698-
Returns the ASN.1 encoded TLS session or `undefined` if no session was
699-
negotiated. Can be used to speed up handshake establishment when reconnecting
700-
to the server.
791+
* {Buffer}
792+
793+
Returns the TLS session data or `undefined` if no session was
794+
negotiated. On the client, the data can be provided to the `session` option of
795+
[`tls.connect()`][] to resume the connection. On the server, it may be useful
796+
for debugging.
797+
798+
See [Session Resumption][] for more information.
701799

702800
### tlsSocket.getTLSTicket()
703801
<!-- YAML
704802
added: v0.11.4
705803
-->
706804

707-
Returns the TLS session ticket or `undefined` if no session was negotiated.
805+
* {Buffer}
806+
807+
For a client, returns the TLS session ticket if one is available, or
808+
`undefined`. For a server, always returns `undefined`.
809+
810+
It may be useful for debugging.
708811

709-
This only works with client TLS sockets. Useful only for debugging, for session
710-
reuse provide `session` option to [`tls.connect()`][].
812+
See [Session Resumption][] for more information.
711813

712814
### tlsSocket.localAddress
713815
<!-- YAML
@@ -1162,18 +1264,17 @@ changes:
11621264
* `requestCert` {boolean} If `true` the server will request a certificate from
11631265
clients that connect and attempt to verify that certificate. **Default:**
11641266
`false`.
1165-
* `sessionTimeout` {number} An integer specifying the number of seconds after
1166-
which the TLS session identifiers and TLS session tickets created by the
1167-
server will time out. See [`SSL_CTX_set_timeout`] for more details.
1267+
* `sessionTimeout` {number} The number of seconds after which a TLS session
1268+
created by the server will no longer be resumable. See
1269+
[Session Resumption][] for more information. **Default:** `300`.
11681270
* `SNICallback(servername, cb)` {Function} A function that will be called if
11691271
the client supports SNI TLS extension. Two arguments will be passed when
11701272
called: `servername` and `cb`. `SNICallback` should invoke `cb(null, ctx)`,
11711273
where `ctx` is a `SecureContext` instance. (`tls.createSecureContext(...)`
11721274
can be used to get a proper `SecureContext`.) If `SNICallback` wasn't
11731275
provided the default callback with high-level API will be used (see below).
1174-
* `ticketKeys`: A 48-byte `Buffer` instance consisting of a 16-byte prefix,
1175-
a 16-byte HMAC key, and a 16-byte AES key. This can be used to accept TLS
1176-
session tickets on multiple instances of the TLS server.
1276+
* `ticketKeys`: {Buffer} 48-bytes of cryptographically strong pseudo-random
1277+
data. See [Session Resumption][] for more information.
11771278
* ...: Any [`tls.createSecureContext()`][] option can be provided. For
11781279
servers, the identity options (`pfx` or `key`/`cert`) are usually required.
11791280
* `secureConnectionListener` {Function}
@@ -1348,21 +1449,26 @@ secureSocket = tls.TLSSocket(socket, options);
13481449

13491450
where `secureSocket` has the same API as `pair.cleartext`.
13501451

1452+
[`'newSession'`]: #tls_event_newsession
1453+
[`'resumeSession'`]: #tls_event_resumesession
13511454
[`'secureConnect'`]: #tls_event_secureconnect
13521455
[`'secureConnection'`]: #tls_event_secureconnection
13531456
[`--tls-cipher-list`]: cli.html#cli_tls_cipher_list_list
13541457
[`NODE_OPTIONS`]: cli.html#cli_node_options_options
1355-
[`SSL_CTX_set_timeout`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_timeout.html
13561458
[`crypto.getCurves()`]: crypto.html#crypto_crypto_getcurves
13571459
[`dns.lookup()`]: dns.html#dns_dns_lookup_hostname_options_callback
13581460
[`net.Server.address()`]: net.html#net_server_address
13591461
[`net.Server`]: net.html#net_class_net_server
13601462
[`net.Socket`]: net.html#net_class_net_socket
13611463
[`server.getConnections()`]: net.html#net_server_getconnections_callback
1464+
[`server.getTicketKeys()`]: #tls_server_getticketkeys
13621465
[`server.listen()`]: net.html#net_server_listen
1466+
[`server.setTicketKeys()`]: #tls_server_setticketkeys_keys
13631467
[`tls.DEFAULT_ECDH_CURVE`]: #tls_tls_default_ecdh_curve
13641468
[`tls.Server`]: #tls_class_tls_server
13651469
[`tls.TLSSocket.getPeerCertificate()`]: #tls_tlssocket_getpeercertificate_detailed
1470+
[`tls.TLSSocket.getSession()`]: #tls_tlssocket_getsession
1471+
[`tls.TLSSocket.getTLSTicket()`]: #tls_tlssocket_gettlsticket
13661472
[`tls.TLSSocket`]: #tls_class_tls_tlssocket
13671473
[`tls.connect()`]: #tls_tls_connect_options_callback
13681474
[`tls.createSecureContext()`]: #tls_tls_createsecurecontext_options
@@ -1376,10 +1482,12 @@ where `secureSocket` has the same API as `pair.cleartext`.
13761482
[OpenSSL Options]: crypto.html#crypto_openssl_options
13771483
[OpenSSL cipher list format documentation]: https://www.openssl.org/docs/man1.1.0/apps/ciphers.html#CIPHER-LIST-FORMAT
13781484
[Perfect Forward Secrecy]: #tls_perfect_forward_secrecy
1485+
[RFC 2246]: https://www.ietf.org/rfc/rfc2246.txt
1486+
[RFC 5077]: https://tools.ietf.org/html/rfc5077
13791487
[RFC 5929]: https://tools.ietf.org/html/rfc5929
13801488
[SSL_METHODS]: https://www.openssl.org/docs/man1.1.0/ssl/ssl.html#Dealing-with-Protocol-Methods
1489+
[Session Resumption]: #tls_session_resumption
13811490
[Stream]: stream.html#stream_stream
1382-
[TLS Session Tickets]: https://www.ietf.org/rfc/rfc5077.txt
13831491
[TLS recommendations]: https://wiki.mozilla.org/Security/Server_Side_TLS
13841492
[asn1.js]: https://www.npmjs.com/package/asn1.js
13851493
[modifying the default cipher suite]: #tls_modifying_the_default_tls_cipher_suite

0 commit comments

Comments
 (0)