Skip to content

Commit 67e40ec

Browse files
committed
doc: tls API for direct TLS socket use
Direct use of tls.TLSSocket to start a TLS session over an existing TCP connection was documented. However, to use this connection securely it is necessary to validate and authenticate the peer's certificate, and the documented events and properties are implemented only for TLSSockets returned by tls.connect(). In order to create secure connections, additional undocumented APIs must be used, and these APIs are being called right now by npm modules. Fix: nodejs#10555 Fix: nodejs#11467
1 parent dfa8abe commit 67e40ec

File tree

2 files changed

+107
-7
lines changed

2 files changed

+107
-7
lines changed

doc/api/tls.md

Lines changed: 99 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,11 @@ connection is open.
463463
added: v0.11.4
464464
-->
465465

466-
* `socket` {net.Socket} An instance of [`net.Socket`][]
466+
* `socket` {net.Socket} An instance of [`net.Socket`][]. Optional, by default
467+
a new TCP or Pipe object will be created, unconnected (it is the caller's
468+
responsibility to connect it). Its unusual to not provide a pre-existing
469+
`socket` when directly creating a `tls.TLSSocket`. The auto-creation feature
470+
is mostly intended for internal use.
467471
* `options` {Object}
468472
* `isServer`: The SSL/TLS protocol is asymetrical, TLSSockets must know if
469473
they are to behave as a server or a client. If `true` the TLS socket will be
@@ -481,6 +485,8 @@ added: v0.11.4
481485
* `requestOCSP` {boolean} If `true`, specifies that the OCSP status request
482486
extension will be added to the client hello and an `'OCSPResponse'` event
483487
will be emitted on the socket before establishing a secure communication
488+
* `pipe`: Optional. When `socket` is not passed, a Pipe will be created if
489+
true, and a TCP socket will be created otherwise.
484490
* `secureContext`: Optional TLS context object created with
485491
[`tls.createSecureContext()`][]. If a `secureContext` is _not_ provided, one
486492
will be created by passing the entire `options` object to
@@ -490,7 +496,18 @@ added: v0.11.4
490496
* ...: Optional [`tls.createSecureContext()`][] options can be provided, see
491497
the `secureContext` option for more information.
492498

493-
Construct a new `tls.TLSSocket` object from an existing TCP socket.
499+
Directly construct a new `tls.TLSSocket` object on top of an underlying
500+
socket.
501+
502+
Always prefer calling [`tls.connect()`][] to direct construction, unless a
503+
socket already exists, and a TLS connection is to be initiated on it. This
504+
latter case is common when implementing protocols like SMTP, that can start off
505+
insecure, and then initiate a secure channel over the existing TCP connection.
506+
507+
***Warning***: When directly constructing a `tls.TLSSocket` instead of using
508+
[`tls.connect()`][] it is the caller's responsibility to:
509+
- manage the lifetime of the the underlying socket, including connecting it;
510+
- validate the peer certificate and identity, see the [`'secure'`][] event.
494511

495512
### Event: 'OCSPResponse'
496513
<!-- YAML
@@ -512,7 +529,7 @@ added: v0.11.4
512529
-->
513530

514531
The `'secureConnect'` event is emitted after the handshaking process for a new
515-
connection has successfully completed. The listener callback will be called
532+
connection has successfully completed. The event will be emitted
516533
regardless of whether or not the server's certificate has been authorized. It
517534
is the client's responsibility to check the `tlsSocket.authorized` property to
518535
determine if the server certificate was signed by one of the specified CAs. If
@@ -521,6 +538,38 @@ determine if the server certificate was signed by one of the specified CAs. If
521538
the `tlsSocket.alpnProtocol` or `tlsSocket.npnProtocol` properties can be
522539
checked to determine the negotiated protocol.
523540

541+
***Note***: Only for sockets created using `tls.connect()`.
542+
543+
### Event: 'secure'
544+
<!-- YAML
545+
added: XXX
546+
-->
547+
548+
Emitted after the handshake is complete.
549+
550+
***Warning***: Before using the connection, the user *must* make the following
551+
checks or the connection should be considered completely insecure:
552+
553+
1. Verify that the peer certificate is valid, see [`ssl.verifyError()`][].
554+
2. Verify that the peer certificate is for the expected host, see
555+
[`tls.checkServerIdentity()`][] and [`tls.TLSSocket.getPeerCertificate()`][].
556+
557+
Example:
558+
559+
```
560+
tlsSocket.on('secure', function() {
561+
const err = this.ssl.verifyError() ||
562+
tls.checkServerIdentity(hostname, this.getPeerCertificate());
563+
if (err)
564+
this.emit('error', err);
565+
});
566+
```
567+
568+
### Event: 'connect'
569+
<!-- YAML
570+
added: v0.11.4
571+
-->
572+
524573
### tlsSocket.address()
525574
<!-- YAML
526575
added: v0.11.4
@@ -539,6 +588,8 @@ added: v0.11.4
539588
Returns `true` if the peer certificate was signed by one of the CAs specified
540589
when creating the `tls.TLSSocket` instance, otherwise `false`.
541590

591+
***Note***: Only for sockets created using `tls.connect()`.
592+
542593
### tlsSocket.authorizationError
543594
<!-- YAML
544595
added: v0.11.4
@@ -547,6 +598,8 @@ added: v0.11.4
547598
Returns the reason why the peer's certificate was not been verified. This
548599
property is set only when `tlsSocket.authorized === false`.
549600

601+
***Note***: Only for sockets created using `tls.connect()`.
602+
550603
### tlsSocket.encrypted
551604
<!-- YAML
552605
added: v0.11.4
@@ -741,6 +794,21 @@ and their processing can be delayed due to packet loss or reordering. However,
741794
smaller fragments add extra TLS framing bytes and CPU overhead, which may
742795
decrease overall server throughput.
743796

797+
### tlsSocket.ssl.verifyError()
798+
<!-- YAML
799+
added: XXX
800+
-->
801+
802+
Returns an `Error` object if the peer's certificate fails validation. Validation
803+
includes many checks, but including
804+
that the certificate is either trusted or can be chained to a trusted
805+
CA (see the `ca` option of [`tls.createSecureContext()`][] for more information).
806+
807+
***Warning***: Validation explicitly does *not* include any authentication of
808+
the identity. [`tls.checkServerIdentity()`][] can be used to authenticate the
809+
identity of the peer.
810+
811+
744812
## tls.connect(port[, host][, options][, callback])
745813
<!-- YAML
746814
added: v0.11.3
@@ -806,9 +874,10 @@ added: v0.11.3
806874
* `servername`: {string} Server name for the SNI (Server Name Indication) TLS
807875
extension.
808876
* `checkServerIdentity(servername, cert)` {Function} A callback function
809-
to be used when checking the server's hostname against the certificate.
810-
This should throw an error if verification fails. The method should return
811-
`undefined` if the `servername` and `cert` are verified.
877+
to be used when checking the server's name against the certificate.
878+
The method should return `undefined` if the `servername` and `cert` are
879+
acceptable and an instance of `Error` if they are not. Default value is
880+
[`tls.checkServerIdentity()`][].
812881
* `session` {Buffer} A `Buffer` instance, containing TLS session.
813882
* `minDHSize` {number} Minimum size of the DH parameter in bits to accept a
814883
TLS connection. When a server offers a DH parameter with a size less
@@ -1078,6 +1147,28 @@ This server can be tested by connecting to it using `openssl s_client`:
10781147
openssl s_client -connect 127.0.0.1:8000
10791148
```
10801149

1150+
## tls.checkServerIdentity(host, cert)
1151+
<!-- YAML
1152+
added: XXX
1153+
-->
1154+
1155+
* host: {String) The hostname that the `cert` should certify.
1156+
* cert: {Object} Object representing the peer's certificate.
1157+
1158+
Check's that the certificate was issued for `host`.
1159+
1160+
Returns `undefined` if `host` matches any of the certificate's names. If it does
1161+
not match, it returns an Error with the following additional properties:
1162+
- reason: {String} Describe why the certificate does not match the host.
1163+
- host: {String} The `host`.
1164+
- cert: {Object} The `cert`.
1165+
1166+
Example:
1167+
```
1168+
if(!tls.checkServerIdentity('example.com', tlsSock.getPeerCertificate())
1169+
// Peer is not authentic, do not trust the peer is 'example.com'!
1170+
```
1171+
10811172
## tls.getCiphers()
10821173
<!-- YAML
10831174
added: v0.10.2
@@ -1223,6 +1314,7 @@ where `secure_socket` has the same API as `pair.cleartext`.
12231314
[Stream]: stream.html#stream_stream
12241315
[TLS Session Tickets]: https://www.ietf.org/rfc/rfc5077.txt
12251316
[TLS recommendations]: https://wiki.mozilla.org/Security/Server_Side_TLS
1317+
[`'secure'`]: #tls_event_secure
12261318
[`'secureConnect'`]: #tls_event_secureconnect
12271319
[`'secureConnection'`]: #tls_event_secureconnection
12281320
[`crypto.getCurves()`]: crypto.html#crypto_crypto_getcurves
@@ -1236,6 +1328,7 @@ where `secure_socket` has the same API as `pair.cleartext`.
12361328
[`tls.createSecureContext()`]: #tls_tls_createsecurecontext_options
12371329
[`tls.createSecurePair()`]: #tls_tls_createsecurepair_context_isserver_requestcert_rejectunauthorized_options
12381330
[`tls.createServer()`]: #tls_tls_createserver_options_secureconnectionlistener
1331+
[`tls.checkServerIdentity()`]: #tls_tls_checkserveridentity_host_cert
12391332
[asn1.js]: https://npmjs.org/package/asn1.js
12401333
[modifying the default cipher suite]: #tls_modifying_the_default_tls_cipher_suite
12411334
[specific attacks affecting larger AES key sizes]: https://www.schneier.com/blog/archives/2009/07/another_new_aes.html

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,16 @@ test(undefined, (err) => {
2020

2121
test({}, (err) => {
2222
assert.strictEqual(err.message, 'unable to verify the first certificate');
23+
// Properties never set on success or failure when tls.connect not used.
24+
assert(!('authorized' in this), 'only supported for tls.connect');
25+
assert(!('authorizationError' in this), 'only supported for tls.connect');
2326
});
2427

2528
test({secureContext: tls.createSecureContext({ca: keys.agent1.ca})}, (err) => {
2629
assert.ifError(err);
30+
// Properties never set on success or failure when tls.connect not used.
31+
assert(!('authorized' in this), 'only supported for tls.connect');
32+
assert(!('authorizationError' in this), 'only supported for tls.connect');
2733
});
2834

2935
test({ca: keys.agent1.ca}, (err) => {
@@ -62,8 +68,9 @@ function test(client, callback) {
6268
.on('connect', common.mustCall(function() {
6369
this.end('hello');
6470
}))
71+
.on('secureConnect', () => assert(0, 'only supported for tls.connect'))
6572
.on('secure', common.mustCall(function() {
66-
callback(this.ssl.verifyError());
73+
callback.call(this, this.ssl.verifyError());
6774
}));
6875
});
6976
}

0 commit comments

Comments
 (0)