Skip to content

tls: deprecate undocumented tlsSocket.ssl property, add docs #23915

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions doc/api/deprecations.md
Original file line number Diff line number Diff line change
Expand Up @@ -2292,6 +2292,16 @@ Type: Runtime

Please use `Server.prototype.setSecureContext()` instead.

<a id="DEP00XX"></a>
### DEP00XX: TLSSocket.prototype.ssl
<!-- YAML
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/23915
description: Runtime deprecation.
-->

`tlsSocket.ssl` is deprecated.

[`--pending-deprecation`]: cli.html#cli_pending_deprecation
[`Buffer.allocUnsafeSlow(size)`]: buffer.html#buffer_class_method_buffer_allocunsafeslow_size
Expand Down
73 changes: 71 additions & 2 deletions doc/api/tls.md
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,8 @@ changes:
* `socket` {net.Socket|stream.Duplex}
On the server side, any `Duplex` stream. On the client side, any
instance of [`net.Socket`][] (for generic `Duplex` stream support
on the client side, [`tls.connect()`][] must be used).
on the client side, [`tls.connect()`][] must be used). If the `socket`
is not provided, a new unconnected TCP socket or named pipe will be created.
* `options` {Object}
* `isServer`: The SSL/TLS protocol is asymmetrical, TLSSockets must know if
they are to behave as a server or a client. If `true` the TLS socket will be
Expand All @@ -489,7 +490,24 @@ changes:
* ...: [`tls.createSecureContext()`][] options that are used if the
`secureContext` option is missing. Otherwise, they are ignored.

Construct a new `tls.TLSSocket` object from an existing TCP socket.
Construct a new `tls.TLSSocket` object from an existing `net.Socket` instance.
Using the `tls.connect()` method is preferred when creating a new TLS session
on top of a new `net.Socket` instance.

Using the `tls.TLSSocket()` constructor directly is helpful when implementing
protocols that can start off insecure (such as SMTP), then initiating a secure
session after the socket is connected. It is important to remember, however,
that it is the caller's responsibility to manage the lifecycle of the provided
`net.Socket`, including establishing the connection and validating peer
certificates and identity. See the [`'secure'`][] event.

### Event: 'connect'
<!-- YAML
added: v0.11.4
-->

The `'connect'` event is emitted once the underlying `net.Socket` has been
connected.

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

### Event: 'secure'
<!-- YAML
added: v0.11.3
-->

The `'secure'` event is emitted after the TLS handshake has been completed.

Before using the connection, the user must verify that the peer certificate
is valid (see [`tls.TLSSocket.verifyError()`][]) and that the peer certificate
is for the expected host (see [`tls.checkServerIdentity()`][] and
[`tls.TLSSocket.getPeerCertificate()`][]). If these checks are not performed,
the connection should be considered insecure. When using the `tls.connect()`
method to create a new `tls.TLSSocket`, these checks are performed
automatically.

```js
tlsSocket.on('secure', function() {
const err = this.verifyError() ||
tls.checkServerIdentity(hostname, this.getPeerCertificate());
if (err)
this.destroy(err);
});
```

### Event: 'secureConnect'
<!-- YAML
added: v0.11.4
Expand All @@ -520,6 +562,9 @@ determine if the server certificate was signed by one of the specified CAs. If
`tlsSocket.alpnProtocol` property can be checked to determine the negotiated
protocol.

The `'secureConnect'` event is only emitted on `tls.TLSSocket` connections
created using the `tls.connect()` method.

### tlsSocket.address()
<!-- YAML
added: v0.11.4
Expand All @@ -539,6 +584,9 @@ added: v0.11.4
Returns the reason why the peer's certificate was not been verified. This
property is set only when `tlsSocket.authorized === false`.

The `tlsSocket.authorizationError` property is only set for `tls.TLSSocket`
instances created using the `tls.connect()` method.

### tlsSocket.authorized
<!-- YAML
added: v0.11.4
Expand All @@ -549,6 +597,9 @@ added: v0.11.4
Returns `true` if the peer certificate was signed by one of the CAs specified
when creating the `tls.TLSSocket` instance, otherwise `false`.

The `tlsSocket.authorized` property is only set for `tls.TLSSocket` instances
created using the `tls.connect()` method.

### tlsSocket.disableRenegotiation()
<!-- YAML
added: v8.4.0
Expand Down Expand Up @@ -805,6 +856,21 @@ and their processing can be delayed due to packet loss or reordering. However,
smaller fragments add extra TLS framing bytes and CPU overhead, which may
decrease overall server throughput.

### tlsSocket.verifyError()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry if these questions are naïve or otherwise ill-informed: Is this the best name for this? I know it's what we used previously, but it wasn't exposed publicly, was it? Might `validatePeerCertificate() or something like that be a better name?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with whatever name folks would like to see.

<!-- YAML
added: v0.11.3
-->

* Returns: {Error} object if the peer's certificate fails validation.

Validation contains many checks, including verification that the certificate
is either trusted or can be chained to a trusted certificate authority
(see the `ca` option of [`tls.createSecureContext()`][] for more information).

Validation explicitly does *not* include any authentication of the identity.
The [`tls.checkServerIdentity()`][] method can be used to authenticate the
identity of the peer.

## tls.checkServerIdentity(hostname, cert)
<!-- YAML
added: v0.8.4
Expand Down Expand Up @@ -1389,6 +1455,7 @@ secureSocket = tls.TLSSocket(socket, options);

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

[`'secure'`]: #tls_event_secure
[`'secureConnect'`]: #tls_event_secureconnect
[`'secureConnection'`]: #tls_event_secureconnection
[`SSL_CTX_set_timeout`]: https://www.openssl.org/docs/man1.1.0/ssl/SSL_CTX_set_timeout.html
Expand All @@ -1403,6 +1470,8 @@ where `secureSocket` has the same API as `pair.cleartext`.
[`tls.Server`]: #tls_class_tls_server
[`tls.TLSSocket.getPeerCertificate()`]: #tls_tlssocket_getpeercertificate_detailed
[`tls.TLSSocket`]: #tls_class_tls_tlssocket
[`tls.TLSSocket.verifyError()`]: #tls_tlssocket_verifyerror
[`tls.checkServerIdentity()`]: #tls_tls_checkserveridentity_hostname_cert
[`tls.connect()`]: #tls_tls_connect_options_callback
[`tls.createSecureContext()`]: #tls_tls_createsecurecontext_options
[`tls.createSecurePair()`]: #tls_tls_createsecurepair_context_isserver_requestcert_rejectunauthorized_options
Expand Down
35 changes: 26 additions & 9 deletions lib/_tls_wrap.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ const kErrorEmitted = Symbol('error-emitted');
const kHandshakeTimeout = Symbol('handshake-timeout');
const kRes = Symbol('res');
const kSNICallback = Symbol('snicallback');
const kHandle = Symbol('ssl');

const noop = () => {};

Expand Down Expand Up @@ -325,7 +326,17 @@ function TLSSocket(socket, opts) {
});

// Proxy for API compatibility
this.ssl = this._handle;
this[kHandle] = this._handle;
Object.defineProperty(this, 'ssl', {
enumerable: true,
configurable: true,
get: util.deprecate(() => this[kHandle],
'The tlsSocket.ssl property is deprecated.',
'DEP00XX'),
set: util.deprecate((val) => this[kHandle] = val,
'The tlsSocket.ssl property is deprecated.',
'DEP00XX')
});

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

Expand Down Expand Up @@ -366,8 +377,8 @@ for (var n = 0; n < proxiedMethods.length; n++) {
tls_wrap.TLSWrap.prototype.close = function close(cb) {
let ssl;
if (this[owner_symbol]) {
ssl = this[owner_symbol].ssl;
this[owner_symbol].ssl = null;
ssl = this[owner_symbol][kHandle];
this[owner_symbol][kHandle] = null;
}

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

TLSSocket.prototype._destroySSL = function _destroySSL() {
if (!this.ssl) return;
this.ssl.destroySSL();
if (this.ssl._secureContext.singleUse) {
this.ssl._secureContext.context.close();
this.ssl._secureContext.context = null;
if (!this[kHandle]) return;
this[kHandle].destroySSL();
if (this[kHandle]._secureContext.singleUse) {
this[kHandle]._secureContext.context.close();
this[kHandle]._secureContext.context = null;
}
this.ssl = null;
this[kHandle] = null;
};

TLSSocket.prototype._init = function(socket, wrap) {
Expand Down Expand Up @@ -586,6 +597,12 @@ TLSSocket.prototype.renegotiate = function(options, callback) {
return true;
};

TLSSocket.prototype.verifyError = function verifyError(...args) {
if (this.destroyed)
return;
return this._handle.verifyError(...args);
};

TLSSocket.prototype.setMaxSendFragment = function setMaxSendFragment(size) {
return this._handle.setMaxSendFragment(size) === 1;
};
Expand Down
2 changes: 1 addition & 1 deletion test/parallel/test-tls-socket-default-options.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ function test(client, callback) {
this.end('hello');
}))
.on('secure', common.mustCall(function() {
callback(this.ssl.verifyError());
callback(this.verifyError());
}));
});
}
31 changes: 31 additions & 0 deletions test/parallel/test-tls-socket-ssl-deprecated.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Flags: --no-warnings
'use strict';

const common = require('../common');
if (!common.hasCrypto)
common.skip('missing crypto');

const tls = require('tls');
const fixtures = require('../common/fixtures');

const key = fixtures.readKey('agent2-key.pem');
const cert = fixtures.readKey('agent2-cert.pem');

const server = tls.createServer({ key, cert }, (socket) => {
socket.ssl; // This should trigger a deprecation warning
socket.setEncoding('utf8');
socket.pipe(socket);
});
server.listen(0, () => {
const options = { rejectUnauthorized: false };
const socket =
tls.connect(server.address().port, options, common.mustCall(() => {
socket.end('hello');
}));
socket.resume();
socket.on('end', () => server.close());
});

common.expectWarning('DeprecationWarning',
'The tlsSocket.ssl property is deprecated.',
'DEP00XX');
4 changes: 2 additions & 2 deletions test/parallel/test-tls-tlswrap-segfault.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ const server = tls.createServer(options, function(s) {

function putImmediate(client) {
setImmediate(function() {
if (client.ssl) {
const fd = client.ssl.fd;
if (client._handle) {
const fd = client._handle.fd;
assert(!!fd);
putImmediate(client);
} else {
Expand Down