Skip to content

Commit e6da77b

Browse files
sam-githubtargos
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 3af173d commit e6da77b

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

@@ -433,17 +528,15 @@ existing server. Existing connections to the server are not interrupted.
433528
added: v3.0.0
434529
-->
435530

436-
* `keys` {Buffer} The keys used for encryption/decryption of the
437-
[TLS Session Tickets][].
438-
439-
Updates the keys for encryption/decryption of the [TLS Session Tickets][].
531+
* `keys` {Buffer} A 48-byte buffer containing the session ticket keys.
440532

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

444535
Changes to the ticket keys are effective only for future server connections.
445536
Existing or currently pending server connections will use the previous keys.
446537

538+
See [Session Resumption][] for more information.
539+
447540
## Class: tls.TLSSocket
448541
<!-- YAML
449542
added: v0.11.4
@@ -782,19 +875,28 @@ information.
782875
added: v0.11.4
783876
-->
784877

785-
Returns the ASN.1 encoded TLS session or `undefined` if no session was
786-
negotiated. Can be used to speed up handshake establishment when reconnecting
787-
to the server.
878+
* {Buffer}
879+
880+
Returns the TLS session data or `undefined` if no session was
881+
negotiated. On the client, the data can be provided to the `session` option of
882+
[`tls.connect()`][] to resume the connection. On the server, it may be useful
883+
for debugging.
884+
885+
See [Session Resumption][] for more information.
788886

789887
### tlsSocket.getTLSTicket()
790888
<!-- YAML
791889
added: v0.11.4
792890
-->
793891

794-
Returns the TLS session ticket or `undefined` if no session was negotiated.
892+
* {Buffer}
893+
894+
For a client, returns the TLS session ticket if one is available, or
895+
`undefined`. For a server, always returns `undefined`.
896+
897+
It may be useful for debugging.
795898

796-
This only works with client TLS sockets. Useful only for debugging, for session
797-
reuse provide `session` option to [`tls.connect()`][].
899+
See [Session Resumption][] for more information.
798900

799901
### tlsSocket.localAddress
800902
<!-- YAML
@@ -1228,18 +1330,17 @@ changes:
12281330
* `requestCert` {boolean} If `true` the server will request a certificate from
12291331
clients that connect and attempt to verify that certificate. **Default:**
12301332
`false`.
1231-
* `sessionTimeout` {number} An integer specifying the number of seconds after
1232-
which the TLS session identifiers and TLS session tickets created by the
1233-
server will time out. See [`SSL_CTX_set_timeout`] for more details.
1333+
* `sessionTimeout` {number} The number of seconds after which a TLS session
1334+
created by the server will no longer be resumable. See
1335+
[Session Resumption][] for more information. **Default:** `300`.
12341336
* `SNICallback(servername, cb)` {Function} A function that will be called if
12351337
the client supports SNI TLS extension. Two arguments will be passed when
12361338
called: `servername` and `cb`. `SNICallback` should invoke `cb(null, ctx)`,
12371339
where `ctx` is a `SecureContext` instance. (`tls.createSecureContext(...)`
12381340
can be used to get a proper `SecureContext`.) If `SNICallback` wasn't
12391341
provided the default callback with high-level API will be used (see below).
1240-
* `ticketKeys`: A 48-byte `Buffer` instance consisting of a 16-byte prefix,
1241-
a 16-byte HMAC key, and a 16-byte AES key. This can be used to accept TLS
1242-
session tickets on multiple instances of the TLS server.
1342+
* `ticketKeys`: {Buffer} 48-bytes of cryptographically strong pseudo-random
1343+
data. See [Session Resumption][] for more information.
12431344
* ...: Any [`tls.createSecureContext()`][] option can be provided. For
12441345
servers, the identity options (`pfx` or `key`/`cert`) are usually required.
12451346
* `secureConnectionListener` {Function}
@@ -1414,21 +1515,26 @@ secureSocket = tls.TLSSocket(socket, options);
14141515

14151516
where `secureSocket` has the same API as `pair.cleartext`.
14161517

1518+
[`'newSession'`]: #tls_event_newsession
1519+
[`'resumeSession'`]: #tls_event_resumesession
14171520
[`'secureConnect'`]: #tls_event_secureconnect
14181521
[`'secureConnection'`]: #tls_event_secureconnection
14191522
[`--tls-cipher-list`]: cli.html#cli_tls_cipher_list_list
14201523
[`NODE_OPTIONS`]: cli.html#cli_node_options_options
1421-
[`SSL_CTX_set_timeout`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_timeout.html
14221524
[`crypto.getCurves()`]: crypto.html#crypto_crypto_getcurves
14231525
[`dns.lookup()`]: dns.html#dns_dns_lookup_hostname_options_callback
14241526
[`net.Server.address()`]: net.html#net_server_address
14251527
[`net.Server`]: net.html#net_class_net_server
14261528
[`net.Socket`]: net.html#net_class_net_socket
14271529
[`server.getConnections()`]: net.html#net_server_getconnections_callback
1530+
[`server.getTicketKeys()`]: #tls_server_getticketkeys
14281531
[`server.listen()`]: net.html#net_server_listen
1532+
[`server.setTicketKeys()`]: #tls_server_setticketkeys_keys
14291533
[`tls.DEFAULT_ECDH_CURVE`]: #tls_tls_default_ecdh_curve
14301534
[`tls.Server`]: #tls_class_tls_server
14311535
[`tls.TLSSocket.getPeerCertificate()`]: #tls_tlssocket_getpeercertificate_detailed
1536+
[`tls.TLSSocket.getSession()`]: #tls_tlssocket_getsession
1537+
[`tls.TLSSocket.getTLSTicket()`]: #tls_tlssocket_gettlsticket
14321538
[`tls.TLSSocket`]: #tls_class_tls_tlssocket
14331539
[`tls.connect()`]: #tls_tls_connect_options_callback
14341540
[`tls.createSecureContext()`]: #tls_tls_createsecurecontext_options
@@ -1443,10 +1549,12 @@ where `secureSocket` has the same API as `pair.cleartext`.
14431549
[OpenSSL Options]: crypto.html#crypto_openssl_options
14441550
[OpenSSL cipher list format documentation]: https://www.openssl.org/docs/man1.1.0/apps/ciphers.html#CIPHER-LIST-FORMAT
14451551
[Perfect Forward Secrecy]: #tls_perfect_forward_secrecy
1552+
[RFC 2246]: https://www.ietf.org/rfc/rfc2246.txt
1553+
[RFC 5077]: https://tools.ietf.org/html/rfc5077
14461554
[RFC 5929]: https://tools.ietf.org/html/rfc5929
14471555
[SSL_METHODS]: https://www.openssl.org/docs/man1.1.0/ssl/ssl.html#Dealing-with-Protocol-Methods
1556+
[Session Resumption]: #tls_session_resumption
14481557
[Stream]: stream.html#stream_stream
1449-
[TLS Session Tickets]: https://www.ietf.org/rfc/rfc5077.txt
14501558
[TLS recommendations]: https://wiki.mozilla.org/Security/Server_Side_TLS
14511559
[asn1.js]: https://www.npmjs.com/package/asn1.js
14521560
[certificate object]: #tls_certificate_object

0 commit comments

Comments
 (0)