From f8dc7a2c441ca40ff170fba8df60178cbbc50e08 Mon Sep 17 00:00:00 2001 From: Pontus Melke Date: Fri, 26 Aug 2016 11:24:56 +0200 Subject: [PATCH 1/2] Deprecate TRUST_SIGNED_CERTIFICATES TRUST_SIGNED_CERTIFICATES does different things on different platforms. It has now been deprecated and user should use TRUST_CUSTOM_CA_SIGNED_CERTIFICATES instead. --- src/v1/driver.js | 4 +-- src/v1/internal/ch-node.js | 16 ++++++++--- src/v1/internal/ch-websocket.js | 6 ++-- src/v1/internal/connector.js | 2 +- test/internal/tls.test.js | 50 +++++++++++++++++++++++++++++++++ test/v1/examples.test.js | 4 +-- test/v1/tck/steps/tlssteps.js | 4 +-- 7 files changed, 72 insertions(+), 14 deletions(-) diff --git a/src/v1/driver.js b/src/v1/driver.js index eda9e7c29..dabb0919c 100644 --- a/src/v1/driver.js +++ b/src/v1/driver.js @@ -196,7 +196,7 @@ let USER_AGENT = "neo4j-javascript/" + VERSION; * // This means that by default, connections "just work" while still giving you * // good encrypted protection. * // - * // TRUST_SIGNED_CERTIFICATES is the classic approach to trust verification - + * // TRUST_CUSTOM_CA_SIGNED_CERTIFICATES is the classic approach to trust verification - * // whenever we establish an encrypted connection, we ensure the host is using * // an encryption certificate that is in, or is signed by, a certificate listed * // as trusted. In the web bundle, this list of trusted certificates is maintained @@ -204,7 +204,7 @@ let USER_AGENT = "neo4j-javascript/" + VERSION; * trust: "TRUST_ON_FIRST_USE" | "TRUST_SIGNED_CERTIFICATES", * * // List of one or more paths to trusted encryption certificates. This only - * // works in the NodeJS bundle, and only matters if you use "TRUST_SIGNED_CERTIFICATES". + * // works in the NodeJS bundle, and only matters if you use "TRUST_CUSTOM_CA_SIGNED_CERTIFICATES". * // The certificate files should be in regular X.509 PEM format. * // For instance, ['./trusted.pem'] * trustedCertificates: [], diff --git a/src/v1/internal/ch-node.js b/src/v1/internal/ch-node.js index 687aef0ac..6335a0e59 100644 --- a/src/v1/internal/ch-node.js +++ b/src/v1/internal/ch-node.js @@ -104,9 +104,17 @@ function storeFingerprint( serverId, knownHostsPath, fingerprint, cb ) { } const TrustStrategy = { - TRUST_SIGNED_CERTIFICATES : function( opts, onSuccess, onFailure ) { + /** + * @deprecated Since version 1.0. Will be deleted in a future version. TRUST_CUSTOM_CA_SIGNED_CERTIFICATES. + */ + TRUST_SIGNED_CERTIFICATES: function( opts, onSuccess, onFailure ) { + console.log("`TRUST_SIGNED_CERTIFICATES` has been deprecated as option and will be removed in a future version of " + + "the driver. Pleas use `TRUST_CUSTOM_CA_SIGNED_CERTIFICATES` instead."); + return TrustStrategy.TRUST_CUSTOM_CA_SIGNED_CERTIFICATES(opts, onSuccess, onFailure); + }, + TRUST_CUSTOM_CA_SIGNED_CERTIFICATES : function( opts, onSuccess, onFailure ) { if( !opts.trustedCertificates || opts.trustedCertificates.length == 0 ) { - onFailure(newError("You are using TRUST_SIGNED_CERTIFICATES as the method " + + onFailure(newError("You are using TRUST_CUSTOM_CA_SIGNED_CERTIFICATES as the method " + "to verify trust for encrypted connections, but have not configured any " + "trustedCertificates. You must specify the path to at least one trusted " + "X.509 certificate for this to work. Two other alternatives is to use " + @@ -153,7 +161,7 @@ const TrustStrategy = { // do TOFU, and the safe approach is to fail. onFailure(newError("You are using a version of NodeJS that does not " + "support trust-on-first use encryption. You can either upgrade NodeJS to " + - "a newer version, use `trust:TRUST_SIGNED_CERTIFICATES` in your driver " + + "a newer version, use `trust:TRUST_CUSTOM_CA_SIGNED_CERTIFICATES` in your driver " + "config instead, or disable encryption using `encrypted:false`.")); return; } @@ -201,7 +209,7 @@ function connect( opts, onSuccess, onFailure=(()=>null) ) { return TrustStrategy[opts.trust](opts, onSuccess, onFailure); } else { onFailure(newError("Unknown trust strategy: " + opts.trust + ". Please use either " + - "trust:'TRUST_SIGNED_CERTIFICATES' or trust:'TRUST_ON_FIRST_USE' in your driver " + + "trust:'TRUST_CUSTOM_CA_SIGNED_CERTIFICATES' or trust:'TRUST_ON_FIRST_USE' in your driver " + "configuration. Alternatively, you can disable encryption by setting " + "`encrypted:false`. There is no mechanism to use encryption without trust verification, " + "because this incurs the overhead of encryption without improving security. If " + diff --git a/src/v1/internal/ch-websocket.js b/src/v1/internal/ch-websocket.js index 31f34c005..7995425cc 100644 --- a/src/v1/internal/ch-websocket.js +++ b/src/v1/internal/ch-websocket.js @@ -42,12 +42,12 @@ class WebSocketChannel { let scheme = "ws"; if( opts.encrypted ) { - if( (!opts.trust) || opts.trust === "TRUST_SIGNED_CERTIFICATES" ) { + if( (!opts.trust) || opts.trust === "TRUST_SIGNED_CERTIFICATES" || opts.trust === "TRUST_CUSTOM_CA_SIGNED_CERTIFICATES" ) { scheme = "wss"; } else { this._error = newError("The browser version of this driver only supports one trust " + - "strategy, 'TRUST_SIGNED_CERTIFICATES'. "+opts.trust+" is not supported. Please " + - "either use TRUST_SIGNED_CERTIFICATES or disable encryption by setting " + + "strategy, 'TRUST_CUSTOM_CA_SIGNED_CERTIFICATES'. "+opts.trust+" is not supported. Please " + + "either use TRUST_CUSTOM_CA_SIGNED_CERTIFICATES or disable encryption by setting " + "`encrypted:false` in the driver configuration."); return; } diff --git a/src/v1/internal/connector.js b/src/v1/internal/connector.js index 603dbf2eb..ed4ad5467 100644 --- a/src/v1/internal/connector.js +++ b/src/v1/internal/connector.js @@ -433,7 +433,7 @@ function connect( url, config = {}) { // Default to using encryption if trust-on-first-use is available encrypted : (config.encrypted == null) ? hasFeature("trust_on_first_use") : config.encrypted, // Default to using trust-on-first-use if it is available - trust : config.trust || (hasFeature("trust_on_first_use") ? "TRUST_ON_FIRST_USE" : "TRUST_SIGNED_CERTIFICATES"), + trust : config.trust || (hasFeature("trust_on_first_use") ? "TRUST_ON_FIRST_USE" : "TRUST_CUSTOM_CA_SIGNED_CERTIFICATES"), trustedCertificates : config.trustedCertificates || [], knownHosts : config.knownHosts })); diff --git a/test/internal/tls.test.js b/test/internal/tls.test.js index 4cdbd4a6d..4b55b78fe 100644 --- a/test/internal/tls.test.js +++ b/test/internal/tls.test.js @@ -90,6 +90,56 @@ describe('trust-signed-certificates', function() { }); }); +describe('trust-custom-ca-signed-certificates', function() { + + var driver; + + it('should reject unknown certificates', function(done) { + // Assuming we only run this test on NodeJS + if( !NodeChannel.available ) { + done(); + return; + } + + // Given + driver = neo4j.driver("bolt://localhost", neo4j.auth.basic("neo4j", "neo4j"), { + encrypted: true, + trust: "TRUST_CUSTOM_CA_SIGNED_CERTIFICATES", + trustedCertificates: ["test/resources/random.certificate"] + }); + + // When + driver.session().run( "RETURN 1").catch( function(err) { + expect( err.message ).toContain( "Server certificate is not trusted" ); + done(); + }); + }); + + it('should accept known certificates', function(done) { + // Assuming we only run this test on NodeJS with TOFU support + if( !NodeChannel.available ) { + done(); + return; + } + + // Given + driver = neo4j.driver("bolt://localhost", neo4j.auth.basic("neo4j", "neo4j"), { + encrypted: true, + trust: "TRUST_CUSTOM_CA_SIGNED_CERTIFICATES", + trustedCertificates: ["build/neo4j/certificates/neo4j.cert"] + }); + + // When + driver.session().run( "RETURN 1").then( done ); + }); + + afterEach(function(){ + if( driver ) { + driver.close(); + } + }); +}); + describe('trust-on-first-use', function() { var driver; diff --git a/test/v1/examples.test.js b/test/v1/examples.test.js index 01ada8ba1..ac91d46fa 100644 --- a/test/v1/examples.test.js +++ b/test/v1/examples.test.js @@ -357,7 +357,7 @@ describe('examples', function() { var driver = neo4j.driver("bolt://localhost", neo4j.auth.basic("neo4j", "neo4j"), { // Note that trust-on-first-use is not available in the browser bundle, // in NodeJS, trust-on-first-use is the default trust mode. In the browser - // it is TRUST_SIGNED_CERTIFICATES. + // it is TRUST_CUSTOM_CA_SIGNED_CERTIFICATES. trust: "TRUST_ON_FIRST_USE", encrypted:true }); @@ -369,7 +369,7 @@ describe('examples', function() { var neo4j = neo4jv1; // tag::tls-signed[] var driver = neo4j.driver("bolt://localhost", neo4j.auth.basic("neo4j", "neo4j"), { - trust: "TRUST_SIGNED_CERTIFICATES", + trust: "TRUST_CUSTOM_CA_SIGNED_CERTIFICATES", // Configuring which certificates to trust here is only available // in NodeJS. In the browser bundle the browsers list of trusted // certificates is used, due to technical limitations in some browsers. diff --git a/test/v1/tck/steps/tlssteps.js b/test/v1/tck/steps/tlssteps.js index 58b311d39..22d9f1b52 100644 --- a/test/v1/tck/steps/tlssteps.js +++ b/test/v1/tck/steps/tlssteps.js @@ -108,7 +108,7 @@ module.exports = function () { this.Given(/^a driver configured to use a trusted certificate$/, function (callback) { this.config = { encrypted: true, - trust: "TRUST_SIGNED_CERTIFICATES", + trust: "TRUST_CUSTOM_CA_SIGNED_CERTIFICATES", knownHosts: this.knownHosts1, trustedCertificates: ['./test/resources/root.cert'] }; @@ -133,7 +133,7 @@ module.exports = function () { //common name is not set to localhost this.config = { encrypted: true, - trust: "TRUST_SIGNED_CERTIFICATES", + trust: "TRUST_CUSTOM_CA_SIGNED_CERTIFICATES", knownHosts: this.knownHosts1, trustedCertificates: [util.neo4jCert] }; From 01b76b0856863855d49a10d10c151f7d01b76ad5 Mon Sep 17 00:00:00 2001 From: Pontus Melke Date: Fri, 26 Aug 2016 11:51:53 +0200 Subject: [PATCH 2/2] Add option TRUST_SYSTEM_CA_SIGNED_CERTIFICATES --- src/v1/driver.js | 5 ++++- src/v1/internal/ch-node.js | 23 +++++++++++++++++++++++ test/internal/tls.test.js | 25 +++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/v1/driver.js b/src/v1/driver.js index dabb0919c..6d6a28f3a 100644 --- a/src/v1/driver.js +++ b/src/v1/driver.js @@ -201,7 +201,10 @@ let USER_AGENT = "neo4j-javascript/" + VERSION; * // an encryption certificate that is in, or is signed by, a certificate listed * // as trusted. In the web bundle, this list of trusted certificates is maintained * // by the web browser. In NodeJS, you configure the list with the next config option. - * trust: "TRUST_ON_FIRST_USE" | "TRUST_SIGNED_CERTIFICATES", + * // + * // TRUST_SYSTEM_CA_SIGNED_CERTIFICATES meand that you trust whatever certificates + * // are in the default certificate chain of th + * trust: "TRUST_ON_FIRST_USE" | "TRUST_SIGNED_CERTIFICATES" | TRUST_CUSTOM_CA_SIGNED_CERTIFICATES | TRUST_SYSTEM_CA_SIGNED_CERTIFICATES, * * // List of one or more paths to trusted encryption certificates. This only * // works in the NodeJS bundle, and only matters if you use "TRUST_CUSTOM_CA_SIGNED_CERTIFICATES". diff --git a/src/v1/internal/ch-node.js b/src/v1/internal/ch-node.js index 6335a0e59..62930b0a4 100644 --- a/src/v1/internal/ch-node.js +++ b/src/v1/internal/ch-node.js @@ -145,6 +145,29 @@ const TrustStrategy = { socket.on('error', onFailure); return socket; }, + TRUST_SYSTEM_CA_SIGNED_CERTIFICATES : function( opts, onSuccess, onFailure ) { + + let tlsOpts = { + // Because we manually check for this in the connect callback, to give + // a more helpful error to the user + rejectUnauthorized: false + }; + let socket = tls.connect(opts.port, opts.host, tlsOpts, function () { + if (!socket.authorized) { + onFailure(newError("Server certificate is not trusted. If you trust the database you are connecting to, use " + + "TRUST_CUSTOM_CA_SIGNED_CERTIFICATES and add" + + " the signing certificate, or the server certificate, to the list of certificates trusted by this driver" + + " using `neo4j.v1.driver(.., { trustedCertificates:['path/to/certificate.crt']}). This " + + " is a security measure to protect against man-in-the-middle attacks. If you are just trying " + + " Neo4j out and are not concerned about encryption, simply disable it using `encrypted=false` in the driver" + + " options.")); + } else { + onSuccess(); + } + }); + socket.on('error', onFailure); + return socket; + }, TRUST_ON_FIRST_USE : function( opts, onSuccess, onFailure ) { let tlsOpts = { // Because we manually verify the certificate against known_hosts diff --git a/test/internal/tls.test.js b/test/internal/tls.test.js index 4b55b78fe..9a6e38451 100644 --- a/test/internal/tls.test.js +++ b/test/internal/tls.test.js @@ -140,6 +140,31 @@ describe('trust-custom-ca-signed-certificates', function() { }); }); +describe('trust-system-ca-signed-certificates', function() { + + var driver; + + fit('should reject unknown certificates', function(done) { + // Assuming we only run this test on NodeJS + if( !NodeChannel.available ) { + done(); + return; + } + + // Given + driver = neo4j.driver("bolt://localhost", neo4j.auth.basic("neo4j", "neo4j"), { + encrypted: true, + trust: "TRUST_SYSTEM_CA_SIGNED_CERTIFICATES" + }); + + // When + driver.session().run( "RETURN 1").catch( function(err) { + expect( err.message ).toContain( "Server certificate is not trusted" ); + done(); + }); + }); +}); + describe('trust-on-first-use', function() { var driver;