node/test/parallel/test-https-agent-session-injection.js
Fedor Indutny 2e1b41a708
tls: emit session after verifying certificate
Prior to this patch `session` event was emitted after `secure` event on
TLSSocket, but before `secureConnect` event. This is problematic for
`https.Agent` because it must cache session only after verifying the
remote peer's certificate.

Connecting to a server that presents an invalid certificate resulted
in the session being cached after the handshake with the server and
evicted right after a certifiate validation error and socket's
destruction. A request initiated during this narrow window would pick
the faulty session, send it to the malicious server and skip the
verification of the server's certificate.

Fixes: https://hackerone.com/reports/811502
CVE-ID: CVE-2020-8172
PR-URL: https://github.com/nodejs-private/node-private/pull/200
Reviewed-By: Sam Roberts <vieuxtech@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
2020-06-02 20:35:51 +02:00

60 lines
1.5 KiB
JavaScript

'use strict';
const common = require('../common');
const assert = require('assert');
if (!common.hasCrypto)
common.skip('missing crypto');
const https = require('https');
const fixtures = require('../common/fixtures');
const options = {
key: fixtures.readKey('agent1-key.pem'),
// NOTE: Certificate Common Name is 'agent1'
cert: fixtures.readKey('agent1-cert.pem'),
// NOTE: TLS 1.3 creates new session ticket **after** handshake so
// `getSession()` output will be different even if the session was reused
// during the handshake.
secureProtocol: 'TLSv1_2_method'
};
const ca = [ fixtures.readKey('ca1-cert.pem') ];
const server = https.createServer(options, function(req, res) {
res.end('ok');
}).listen(0, common.mustCall(function() {
const port = this.address().port;
const req = https.get({
port,
path: '/',
ca,
servername: 'nodejs.org',
}, common.mustNotCall(() => {}));
req.on('error', common.mustCall((err) => {
assert.strictEqual(
err.message,
'Hostname/IP does not match certificate\'s altnames: ' +
'Host: nodejs.org. is not cert\'s CN: agent1');
const second = https.get({
port,
path: '/',
ca,
servername: 'nodejs.org',
}, common.mustNotCall(() => {}));
second.on('error', common.mustCall((err) => {
server.close();
assert.strictEqual(
err.message,
'Hostname/IP does not match certificate\'s altnames: ' +
'Host: nodejs.org. is not cert\'s CN: agent1');
}));
}));
}));