Skip to content

Commit 83c6b8c

Browse files
committed
Add identify verification option for TLS
Right now the hostname presented on a TLS certificate is never validated. This means that man-in-the-middle attacks are trivial when using for example public CAs to sign certificates. The issue is that anyone can have a public CA sign a certificate for a hostname they own, which is key to the security model there. But with the current functionality in the `mysql2` driver, the hostname is never verified. This means an attacker could do a man in the middle attack and only has to present a signed certificate but doesn't have to match the hostname. It does not only apply to public CAs in system roots, but also for example Amazon RDS where a single root chain is also used. The changes here add an option to explicitly verify the identity on the certificate. This option is available in other MySQL drivers as well, such as `libmysqlclient` and everything based on that (often drivers for Ruby, Python etc.). Also other languages like Go, Java, .Net etc. provide an option to have strict identity verification. Ideally of course, this validation would be the default and always enforced, but that also would likely be a significant breaking change, so right now in the code here the setting is opt-in for having a secure connection to MySQL. Additionally, it adds SNI on the connection which is a best practice as well.
1 parent fce1a49 commit 83c6b8c

File tree

2 files changed

+21
-1
lines changed

2 files changed

+21
-1
lines changed

lib/connection.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,13 +342,19 @@ class Connection extends EventEmitter {
342342
minVersion: this.config.ssl.minVersion
343343
});
344344
const rejectUnauthorized = this.config.ssl.rejectUnauthorized;
345+
const verifyIdentity = this.config.ssl.verifyIdentity;
346+
const host = this.config.host;
347+
345348
let secureEstablished = false;
346349
const secureSocket = new Tls.TLSSocket(this.stream, {
347350
rejectUnauthorized: rejectUnauthorized,
348351
requestCert: true,
349352
secureContext: secureContext,
350353
isServer: false
351354
});
355+
if (typeof host === 'string') {
356+
secureSocket.setServername(host);
357+
}
352358
// error handler for secure socket
353359
secureSocket.on('_tlsError', err => {
354360
if (secureEstablished) {
@@ -359,7 +365,15 @@ class Connection extends EventEmitter {
359365
});
360366
secureSocket.on('secure', () => {
361367
secureEstablished = true;
362-
onSecure(rejectUnauthorized ? secureSocket.ssl.verifyError() : null);
368+
let callbackValue = null;
369+
if (rejectUnauthorized) {
370+
callbackValue = secureSocket.ssl.verifyError()
371+
if (!callbackValue && typeof host === 'string' && verifyIdentity) {
372+
const cert = secureSocket.ssl.getPeerCertificate(true);
373+
callbackValue = Tls.checkServerIdentity(host, cert)
374+
}
375+
}
376+
onSecure(callbackValue);
363377
});
364378
secureSocket.on('data', data => {
365379
this.packetParser.execute(data);

typings/mysql/lib/Connection.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,12 @@ declare namespace Connection {
226226
* Configure the minimum supported version of SSL, the default is TLSv1.2.
227227
*/
228228
minVersion?: string;
229+
230+
/**
231+
* You can verify the server name identity presented on the server certificate when connecting to a MySQL server.
232+
* You should enable this but it is disabled by default right now for backwards compatibility.
233+
*/
234+
verifyIdentity?: boolean;
229235
}
230236
}
231237

0 commit comments

Comments
 (0)