Node.js Driver failing to connect due to "unsafe legacy renegotiation disabled"
Overview
In this community forum post there was a report of the MongoDB Node.js driver failing to connect with the following error:
MongoServerSelectionError: C8320000:error:0A000152:SSL routines:final_renegotiate:unsafe legacy renegotiation disabled:c:\ws\deps\openssl\openssl\ssl\statem\extensions.c:922
This error doesn’t smell like a MongoDB-specific error, so digging into “final_renegotiate:unsafe legacy renegotiation disabled
“ specifically lead to this openssl
issue that looks to elaborate on the meaning of the error message:
TLSv1.2 (and earlier) support the concept of renegotiation. In 2009 (i.e. after the TLSv1.2 RFC was published), a flaw was discovered with how renegotiation works that could lead to an attack. After the attack was discovered a fix was deployed to all TLS libraries. In order for the fixed version of renegotiation to work both the client and the server need to support it.
The original (unfixed) version of renegotiation is known as “unsafe legacy renegotiation” in OpenSSL. The fixed version is known as “secure renegotiation”. So either a peer does not have the fix, in which case it will be using “unsafe legacy renegotiation”, or it does have the fix in which case it will be using “secure renegotiation”.
So it seems that the error originated from OpenSSL, and “that flaw” they’re alluding to was likely CVE-2009-3555. What was particularly interesting about this issue is that it only occurred when the application was run using Node.js 20, while Node.js 16 didn’t exhibit any issues - so what’s different between those two versions? One notable change is that Node.js 17+ use OpenSSL 3.0 by default - and starting with 3.0 secure negotiation support is required by default.
For more information on secure server-side renegotiation I’d highly recommend this discussion.
Configuring OpenSSL via the Node.js Driver
A similar issue was reported on Stack Overflow for the axios
library, and the solution there was to pass secureOptions: crypto.constants.SSL_OP_LEGACY_SERVER_CONNECT
during request creation. As secureOptions
is an option passed to Node’s tls.createSecureContext
API (which MongoDB documents an example of using) it should be possible to do something similar with the Node.js driver.
1
2
3
4
5
6
7
8
import { MongoClient } from 'mongodb';
import { * as crypto } from 'crypto';
const client = new MongoClient("mongodb+srv://...", {
secureContext: {
secureOptions: crypto.constants.SSL_OP_LEGACY_SERVER_CONNECT
}
});
SUCCESS! The above example allows a SecureContext
object to be created with the secureOptions
selected from the enumerated OpenSSL options Node.js has defined.
Though the Node.js driver allows direct configuration of the
SecureContext
object, as other MongoDB drivers may not, DRIVERS-2823 is being considered to ensure this type of configuration is available.
Alternative Configuration
Configuring the MongoDB Node.js driver’s OpenSSL options directly is likely the preferred approach, but the Node runtime can also be configured (via --openssl-config=file
). In this, when the node
process is executed the path to a custom OpenSSL configuration file could be provided as follows:
1
node --openssl-config=/path/to/openssl.conf
Where openssl.conf
is setup similar to the example below:
1
2
3
4
5
6
7
8
9
10
nodejs_conf = openssl_init
[openssl_init]
ssl_conf = ssl_sect
[ssl_sect]
system_default = system_default_sect
[system_default_sect]
Options = UnsafeLegacyRenegotiation
Comments powered by Disqus.