Skip to content

Commit 4ca0e4c

Browse files
committed
tls: deprecate undocumented .ssl property, add docs
deprecate the legacy undocumented `.ssl` alias for the `TLSSocket._handle` and document alternatives. Document how to properly use the `TLSSocket` constructor directly. Updated take on nodejs#10846 Fixes: nodejs#10555
1 parent 98819df commit 4ca0e4c

6 files changed

+141
-14
lines changed

doc/api/deprecations.md

+10
Original file line numberDiff line numberDiff line change
@@ -2292,6 +2292,16 @@ Type: Runtime
22922292
22932293
Please use `Server.prototype.setSecureContext()` instead.
22942294
2295+
<a id="DEP00XX"></a>
2296+
### DEP00XX: TLSSocket.prototype.ssl
2297+
<!-- YAML
2298+
changes:
2299+
- version: REPLACEME
2300+
pr-url: https://github.com/nodejs/node/pull/23915
2301+
description: Runtime deprecation.
2302+
-->
2303+
2304+
The undocumented `tlsSocket.ssl` property has been deprecated.
22952305
22962306
[`--pending-deprecation`]: cli.html#cli_pending_deprecation
22972307
[`Buffer.allocUnsafeSlow(size)`]: buffer.html#buffer_class_method_buffer_allocunsafeslow_size

doc/api/tls.md

+71-2
Original file line numberDiff line numberDiff line change
@@ -465,7 +465,8 @@ changes:
465465
* `socket` {net.Socket|stream.Duplex}
466466
On the server side, any `Duplex` stream. On the client side, any
467467
instance of [`net.Socket`][] (for generic `Duplex` stream support
468-
on the client side, [`tls.connect()`][] must be used).
468+
on the client side, [`tls.connect()`][] must be used). If the `socket`
469+
is not provided, a new unconnected TCP socket or named pipe will be created.
469470
* `options` {Object}
470471
* `isServer`: The SSL/TLS protocol is asymmetrical, TLSSockets must know if
471472
they are to behave as a server or a client. If `true` the TLS socket will be
@@ -489,7 +490,24 @@ changes:
489490
* ...: [`tls.createSecureContext()`][] options that are used if the
490491
`secureContext` option is missing. Otherwise, they are ignored.
491492

492-
Construct a new `tls.TLSSocket` object from an existing TCP socket.
493+
Construct a new `tls.TLSSocket` object from an existing `net.Socket` instance.
494+
Using the `tls.connect()` method is preferred when creating a new TLS session
495+
on top of a new `net.Socket` instance.
496+
497+
Using the `tls.TLSSocket()` constructor directly is helpful when implementing
498+
protocols that can start off insecure (such as SMTP), then initiating a secure
499+
session after the socket is connected. It is important to remember, however,
500+
that it is the caller's responsibility to manage the lifecycle of the provided
501+
`net.Socket`, including establishing the connection and validating peer
502+
certificates and identity. See the [`'secure'`][] event.
503+
504+
### Event: 'connect'
505+
<!-- YAML
506+
added: v0.11.4
507+
-->
508+
509+
The `'connect'` event is emitted once the underlying `net.Socket` has been
510+
connected.
493511

494512
### Event: 'OCSPResponse'
495513
<!-- YAML
@@ -505,6 +523,30 @@ The listener callback is passed a single argument when called:
505523
Typically, the `response` is a digitally signed object from the server's CA that
506524
contains information about server's certificate revocation status.
507525

526+
### Event: 'secure'
527+
<!-- YAML
528+
added: v0.11.3
529+
-->
530+
531+
The `'secure'` event is emitted after the TLS handshake has been completed.
532+
533+
Before using the connection, the user must verify that the peer certificate
534+
is valid (see [`tls.TLSSocket.verifyError()`][]) and that the peer certificate
535+
is for the expected host (see [`tls.checkServerIdentity()`][] and
536+
[`tls.TLSSocket.getPeerCertificate()`][]). If these checks are not performed,
537+
the connection should be considered insecure. When using the `tls.connect()`
538+
method to create a new `tls.TLSSocket`, these checks are performed
539+
automatically.
540+
541+
```js
542+
tlsSocket.on('secure', function() {
543+
const err = this.verifyError() ||
544+
tls.checkServerIdentity(hostname, this.getPeerCertificate());
545+
if (err)
546+
this.destroy(err);
547+
});
548+
```
549+
508550
### Event: 'secureConnect'
509551
<!-- YAML
510552
added: v0.11.4
@@ -520,6 +562,9 @@ determine if the server certificate was signed by one of the specified CAs. If
520562
`tlsSocket.alpnProtocol` property can be checked to determine the negotiated
521563
protocol.
522564

565+
The `'secureConnect'` event is only emitted on `tls.TLSSocket` connections
566+
created using the `tls.connect()` method.
567+
523568
### tlsSocket.address()
524569
<!-- YAML
525570
added: v0.11.4
@@ -539,6 +584,9 @@ added: v0.11.4
539584
Returns the reason why the peer's certificate was not been verified. This
540585
property is set only when `tlsSocket.authorized === false`.
541586

587+
The `tlsSocket.authorizationError` property is only set for `tls.TLSSocket`
588+
instances created using the `tls.connect()` method.
589+
542590
### tlsSocket.authorized
543591
<!-- YAML
544592
added: v0.11.4
@@ -549,6 +597,9 @@ added: v0.11.4
549597
Returns `true` if the peer certificate was signed by one of the CAs specified
550598
when creating the `tls.TLSSocket` instance, otherwise `false`.
551599

600+
The `tlsSocket.authorized` property is only set for `tls.TLSSocket` instances
601+
created using the `tls.connect()` method.
602+
552603
### tlsSocket.disableRenegotiation()
553604
<!-- YAML
554605
added: v8.4.0
@@ -805,6 +856,21 @@ and their processing can be delayed due to packet loss or reordering. However,
805856
smaller fragments add extra TLS framing bytes and CPU overhead, which may
806857
decrease overall server throughput.
807858

859+
### tlsSocket.verifyError()
860+
<!-- YAML
861+
added: v0.11.3
862+
-->
863+
864+
* Returns: {Error} object if the peer's certificate fails validation.
865+
866+
Validation contains many checks, including verification that the certificate
867+
is either trusted or can be chained to a trusted certificate authority
868+
(see the `ca` option of [`tls.createSecureContext()`][] for more information).
869+
870+
Validation explicitly does *not* include any authentication of the identity.
871+
The [`tls.checkServerIdentity()`][] method can be used to authenticate the
872+
identity of the peer.
873+
808874
## tls.checkServerIdentity(hostname, cert)
809875
<!-- YAML
810876
added: v0.8.4
@@ -1389,6 +1455,7 @@ secureSocket = tls.TLSSocket(socket, options);
13891455

13901456
where `secureSocket` has the same API as `pair.cleartext`.
13911457

1458+
[`'secure'`]: #tls_event_secure
13921459
[`'secureConnect'`]: #tls_event_secureconnect
13931460
[`'secureConnection'`]: #tls_event_secureconnection
13941461
[`SSL_CTX_set_timeout`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_timeout.html
@@ -1403,6 +1470,8 @@ where `secureSocket` has the same API as `pair.cleartext`.
14031470
[`tls.Server`]: #tls_class_tls_server
14041471
[`tls.TLSSocket.getPeerCertificate()`]: #tls_tlssocket_getpeercertificate_detailed
14051472
[`tls.TLSSocket`]: #tls_class_tls_tlssocket
1473+
[`tls.TLSSocket.verifyError()`]: #tls_tlssocket_verifyerror
1474+
[`tls.checkServerIdentity()`]: #tls_tls_checkserveridentity_hostname_cert
14061475
[`tls.connect()`]: #tls_tls_connect_options_callback
14071476
[`tls.createSecureContext()`]: #tls_tls_createsecurecontext_options
14081477
[`tls.createSecurePair()`]: #tls_tls_createsecurepair_context_isserver_requestcert_rejectunauthorized_options

lib/_tls_wrap.js

+26-9
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ const kErrorEmitted = Symbol('error-emitted');
5656
const kHandshakeTimeout = Symbol('handshake-timeout');
5757
const kRes = Symbol('res');
5858
const kSNICallback = Symbol('snicallback');
59+
const kSSL = Symbol('ssl');
5960

6061
const noop = () => {};
6162

@@ -325,7 +326,17 @@ function TLSSocket(socket, opts) {
325326
});
326327

327328
// Proxy for API compatibility
328-
this.ssl = this._handle;
329+
this[kSSL] = this._handle;
330+
Object.defineProperty(this, 'ssl', {
331+
enumerable: true,
332+
configurable: true,
333+
get: util.deprecate(() => this[kSSL],
334+
'The tlsSocket.ssl property is deprecated.',
335+
'DEP00XX'),
336+
set: util.deprecate((val) => this[kSSL] = val,
337+
'The tlsSocket.ssl property is deprecated.',
338+
'DEP00XX')
339+
});
329340

330341
this.on('error', this._tlsError);
331342

@@ -366,8 +377,8 @@ for (var n = 0; n < proxiedMethods.length; n++) {
366377
tls_wrap.TLSWrap.prototype.close = function close(cb) {
367378
let ssl;
368379
if (this[owner_symbol]) {
369-
ssl = this[owner_symbol].ssl;
370-
this[owner_symbol].ssl = null;
380+
ssl = this[owner_symbol][kSSL];
381+
this[owner_symbol][kSSL] = null;
371382
}
372383

373384
// Invoke `destroySSL` on close to clean up possibly pending write requests
@@ -457,13 +468,13 @@ function destroySSL(self) {
457468
}
458469

459470
TLSSocket.prototype._destroySSL = function _destroySSL() {
460-
if (!this.ssl) return;
461-
this.ssl.destroySSL();
462-
if (this.ssl._secureContext.singleUse) {
463-
this.ssl._secureContext.context.close();
464-
this.ssl._secureContext.context = null;
471+
if (!this[kSSL]) return;
472+
this[kSSL].destroySSL();
473+
if (this[kSSL]._secureContext.singleUse) {
474+
this[kSSL]._secureContext.context.close();
475+
this[kSSL]._secureContext.context = null;
465476
}
466-
this.ssl = null;
477+
this[kSSL] = null;
467478
};
468479

469480
TLSSocket.prototype._init = function(socket, wrap) {
@@ -586,6 +597,12 @@ TLSSocket.prototype.renegotiate = function(options, callback) {
586597
return true;
587598
};
588599

600+
TLSSocket.prototype.verifyError = function verifyError(...args) {
601+
if (this.destroyed)
602+
return;
603+
return this._handle.verifyError(...args);
604+
};
605+
589606
TLSSocket.prototype.setMaxSendFragment = function setMaxSendFragment(size) {
590607
return this._handle.setMaxSendFragment(size) === 1;
591608
};

test/parallel/test-tls-socket-default-options.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ function test(client, callback) {
6161
this.end('hello');
6262
}))
6363
.on('secure', common.mustCall(function() {
64-
callback(this.ssl.verifyError());
64+
callback(this.verifyError());
6565
}));
6666
});
6767
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Flags: --no-warnings
2+
'use strict';
3+
4+
const common = require('../common');
5+
if (!common.hasCrypto)
6+
common.skip('missing crypto');
7+
8+
const tls = require('tls');
9+
const fixtures = require('../common/fixtures');
10+
11+
const key = fixtures.readKey('agent2-key.pem');
12+
const cert = fixtures.readKey('agent2-cert.pem');
13+
14+
const server = tls.createServer({ key, cert }, (socket) => {
15+
socket.ssl; // This should trigger a deprecation warning
16+
socket.setEncoding('utf8');
17+
socket.pipe(socket);
18+
});
19+
server.listen(0, () => {
20+
const options = { rejectUnauthorized: false };
21+
const socket =
22+
tls.connect(server.address().port, options, common.mustCall(() => {
23+
socket.end('hello');
24+
}));
25+
socket.resume();
26+
socket.on('end', () => server.close());
27+
});
28+
29+
common.expectWarning('DeprecationWarning',
30+
'The tlsSocket.ssl property is deprecated.',
31+
'DEP00XX');

test/parallel/test-tls-tlswrap-segfault.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ const server = tls.createServer(options, function(s) {
3131

3232
function putImmediate(client) {
3333
setImmediate(function() {
34-
if (client.ssl) {
35-
const fd = client.ssl.fd;
34+
if (client._handle) {
35+
const fd = client._handle.fd;
3636
assert(!!fd);
3737
putImmediate(client);
3838
} else {

0 commit comments

Comments
 (0)