diff --git a/doc/api/tls.md b/doc/api/tls.md index f98bb297672..9a40aa16f04 100644 --- a/doc/api/tls.md +++ b/doc/api/tls.md @@ -911,6 +911,23 @@ changes: Construct a new `tls.TLSSocket` object from an existing TCP socket. +[`tls.connect()`][] is preferred when creating a new TLS session on top of a +new `net.Socket`. Constructing a `tls.TLSSocket` directly is useful when +implementing protocols that can start insecurely (such as SMTP) and then +upgrade an existing connection to TLS. + +> **Warning**: When constructing a `tls.TLSSocket` directly instead of using +> [`tls.connect()`][], it is the caller's responsibility to: +> +> * manage the lifetime of the underlying socket, including connecting it; +> * validate the peer certificate and identity before treating the connection +> as secure. See the [`'secure'`][] event. +> +> Unlike [`tls.connect()`][], direct construction does not emit +> [`'secureConnect'`][], does not set [`tlsSocket.authorized`][] / +> [`tlsSocket.authorizationError`][] after the handshake, and does not run +> [`tls.checkServerIdentity()`][] automatically. + ### Event: `'keylog'` The `'secure'` event is emitted after the TLS handshake has successfully -completed and a secure connection has been established. +completed. This event is emitted on both client and server {tls.TLSSocket} instances, including sockets created using the `new tls.TLSSocket()` constructor. +Handshake completion alone does not mean the peer was authenticated. When the +socket was created with [`tls.connect()`][], Node.js performs certificate and +identity checks and then emits [`'secureConnect'`][]. When using +`new tls.TLSSocket()` directly, those checks are the caller's responsibility. +Before using the connection, verify: + +1. The peer certificate is trusted, see [`tlsSocket.ssl.verifyError()`][]. +2. The peer certificate matches the expected host, see + [`tls.checkServerIdentity()`][] and [`tls.TLSSocket.getPeerCertificate()`][]. + +If these checks are skipped, the connection should be considered insecure. + +```js +const { checkServerIdentity } = require('node:tls'); + +// `hostname` is the expected server name (for example, 'example.com'). +tlsSocket.on('secure', () => { + const err = tlsSocket.ssl.verifyError() || + checkServerIdentity(hostname, tlsSocket.getPeerCertificate()); + if (err) { + tlsSocket.destroy(err); + } +}); +``` + ### Event: `'secureConnect'` + +* Type: {Object} + +The underlying OpenSSL `TLSWrap` handle for this socket. + +#### `tlsSocket.ssl.verifyError()` + +* Returns: {Error|null} + +Returns an {Error} object describing why certificate verification failed, or +`null` if verification succeeded (OpenSSL `X509_V_OK`). + +This is the certificate-chain verification result from OpenSSL. It does **not** +check that the certificate matches the expected host name; use +[`tls.checkServerIdentity()`][] for that. + +When using [`tls.connect()`][], prefer [`tlsSocket.authorized`][] and +[`tlsSocket.authorizationError`][], which are set after Node.js runs these +checks. For sockets created with `new tls.TLSSocket()`, call `verifyError()` +from a [`'secure'`][] listener before using the connection. + ### `tlsSocket.disableRenegotiation()`